Skip to content

Commit e57746c

Browse files
authored
Add helper functions for SQL(), Pos(), End() (#120)
* Bump Go version to 1.20, and add util.go * Add comments to ast/util.go * Add strOpt in ast/util.go * Refactor (*CallExpr).SQL() * Refactor (*Select).SQL() * Refactor (*Select).End() * Bump Go version in .github/workflows * Refactor (*Path).Pos(), (*Path).End() * Refactor (*Path).SQL() * Remove lastElem * Revert "Remove lastElem" This reverts commit e1a3e81. * Re-organize helper functions
1 parent cdd31d5 commit e57746c

4 files changed

Lines changed: 119 additions & 81 deletions

File tree

‎.github/workflows/go.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313

14-
- name: Set up Go 1.19
15-
uses: actions/setup-go@v5
14+
- name: Set up Go 1.20
15+
uses: actions/setup-go@v4
1616
with:
17-
go-version: 1.19
17+
go-version: "1.20"
1818
id: go
1919

2020
- name: Check out code into the Go module directory

‎ast/pos.go‎

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,51 @@ import (
44
"github.com/cloudspannerecosystem/memefish/token"
55
)
66

7+
// ================================================================================
8+
//
9+
// Helper functions for Pos(), End()
10+
// These functions are intended for use within this file only.
11+
//
12+
// ================================================================================
13+
14+
// lastNode returns last element of Node slice.
15+
// This function corresponds to NodeSliceVar[$] in ast.go.
16+
func lastNode[T Node](s []T) T {
17+
return s[len(s)-1]
18+
}
19+
20+
// firstValidEnd returns the first valid Pos() in argument.
21+
// "valid" means the node is not nil and Pos().Invalid() is not true.
22+
// This function corresponds to "(n0 ?? n1 ?? ...).End()"
23+
func firstValidEnd(ns ...Node) token.Pos {
24+
for _, n := range ns {
25+
if n != nil && !n.End().Invalid() {
26+
return n.End()
27+
}
28+
}
29+
return token.InvalidPos
30+
}
31+
32+
// firstPos returns the Pos() of the first node.
33+
// If argument is an empty slice, this function returns token.InvalidPos.
34+
// This function corresponds to NodeSliceVar[0].pos in ast.go.
35+
func firstPos[T Node](s []T) token.Pos {
36+
if len(s) == 0 {
37+
return token.InvalidPos
38+
}
39+
return s[0].Pos()
40+
}
41+
42+
// lastEnd returns the End() of the last node.
43+
// If argument is an empty slice, this function returns token.InvalidPos.
44+
// This function corresponds to NodeSliceVar[$].end in ast.go.
45+
func lastEnd[T Node](s []T) token.Pos {
46+
if len(s) == 0 {
47+
return token.InvalidPos
48+
}
49+
return lastNode(s).End()
50+
}
51+
752
// ================================================================================
853
//
954
// SELECT
@@ -39,25 +84,7 @@ func (c *CTE) End() token.Pos { return c.Rparen + 1 }
3984
func (s *Select) Pos() token.Pos { return s.Select }
4085

4186
func (s *Select) End() token.Pos {
42-
if s.Limit != nil {
43-
return s.Limit.End()
44-
}
45-
if s.OrderBy != nil {
46-
return s.OrderBy.End()
47-
}
48-
if s.Having != nil {
49-
return s.Having.End()
50-
}
51-
if s.GroupBy != nil {
52-
return s.GroupBy.End()
53-
}
54-
if s.Where != nil {
55-
return s.Where.End()
56-
}
57-
if s.From != nil {
58-
return s.From.End()
59-
}
60-
return s.Results[len(s.Results)-1].End()
87+
return firstValidEnd(s.Limit, s.OrderBy, s.Having, s.GroupBy, s.Where, s.From, lastNode(s.Results))
6188
}
6289

6390
func (c *CompoundQuery) Pos() token.Pos {
@@ -376,8 +403,8 @@ func (p *Param) End() token.Pos { return p.Atmark + 1 + token.Pos(len(p.Name)) }
376403
func (i *Ident) Pos() token.Pos { return i.NamePos }
377404
func (i *Ident) End() token.Pos { return i.NameEnd }
378405

379-
func (p *Path) Pos() token.Pos { return p.Idents[0].Pos() }
380-
func (p *Path) End() token.Pos { return p.Idents[len(p.Idents)-1].End() }
406+
func (p *Path) Pos() token.Pos { return firstPos(p.Idents) }
407+
func (p *Path) End() token.Pos { return lastEnd(p.Idents) }
381408

382409
func (a *ArrayLiteral) Pos() token.Pos {
383410
if !a.Array.Invalid() {

‎ast/sql.go‎

Lines changed: 67 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,59 @@ package ast
22

33
import (
44
"github.com/cloudspannerecosystem/memefish/token"
5+
"strings"
56
)
67

8+
// ================================================================================
9+
//
10+
// Helper functions for SQL()
11+
// These functions are intended for use within this file only.
12+
//
13+
// ================================================================================
14+
15+
// sqlOpt outputs:
16+
//
17+
// when node != nil: left + node.SQL() + right
18+
// else : empty string
19+
//
20+
// This function corresponds to sqlOpt in ast.go
21+
func sqlOpt[T interface {
22+
Node
23+
comparable
24+
}](left string, node T, right string) string {
25+
var zero T
26+
if node == zero {
27+
return ""
28+
}
29+
return left + node.SQL() + right
30+
}
31+
32+
// strOpt outputs:
33+
//
34+
// when pred == true: s
35+
// else : empty string
36+
//
37+
// This function corresponds to {{if pred}}s{{end}} in ast.go
38+
func strOpt(pred bool, s string) string {
39+
if pred {
40+
return s
41+
}
42+
return ""
43+
}
44+
45+
// sqlJoin outputs joined string of SQL() of all elems by sep.
46+
// This function corresponds to sqlJoin in ast.go
47+
func sqlJoin[T Node](elems []T, sep string) string {
48+
var b strings.Builder
49+
for i, r := range elems {
50+
if i > 0 {
51+
b.WriteString(sep)
52+
}
53+
b.WriteString(r.SQL())
54+
}
55+
return b.String()
56+
}
57+
758
type prec int
859

960
const (
@@ -116,36 +167,16 @@ func (c *CTE) SQL() string {
116167
}
117168

118169
func (s *Select) SQL() string {
119-
sql := "SELECT "
120-
if s.Distinct {
121-
sql += "DISTINCT "
122-
}
123-
if s.AsStruct {
124-
sql += "AS STRUCT "
125-
}
126-
sql += s.Results[0].SQL()
127-
for _, r := range s.Results[1:] {
128-
sql += ", " + r.SQL()
129-
}
130-
if s.From != nil {
131-
sql += " " + s.From.SQL()
132-
}
133-
if s.Where != nil {
134-
sql += " " + s.Where.SQL()
135-
}
136-
if s.GroupBy != nil {
137-
sql += " " + s.GroupBy.SQL()
138-
}
139-
if s.Having != nil {
140-
sql += " " + s.Having.SQL()
141-
}
142-
if s.OrderBy != nil {
143-
sql += " " + s.OrderBy.SQL()
144-
}
145-
if s.Limit != nil {
146-
sql += " " + s.Limit.SQL()
147-
}
148-
return sql
170+
return "SELECT " +
171+
strOpt(s.Distinct, "DISTINCT ") +
172+
strOpt(s.AsStruct, "AS STRUCT ") +
173+
sqlJoin(s.Results, ", ") +
174+
sqlOpt(" ", s.From, "") +
175+
sqlOpt(" ", s.Where, "") +
176+
sqlOpt(" ", s.GroupBy, "") +
177+
sqlOpt(" ", s.Having, "") +
178+
sqlOpt(" ", s.OrderBy, "") +
179+
sqlOpt(" ", s.Limit, "")
149180
}
150181

151182
func (c *CompoundQuery) SQL() string {
@@ -464,27 +495,11 @@ func (i *IndexExpr) SQL() string {
464495
}
465496

466497
func (c *CallExpr) SQL() string {
467-
sql := c.Func.SQL() + "("
468-
if c.Distinct {
469-
sql += "DISTINCT "
470-
}
471-
for i, a := range c.Args {
472-
if i != 0 {
473-
sql += ", "
474-
}
475-
sql += a.SQL()
476-
}
477-
if len(c.Args) > 0 && len(c.NamedArgs) > 0 {
478-
sql += ", "
479-
}
480-
for i, v := range c.NamedArgs {
481-
if i != 0 {
482-
sql += ", "
483-
}
484-
sql += v.SQL()
485-
}
486-
sql += ")"
487-
return sql
498+
return c.Func.SQL() + "(" + strOpt(c.Distinct, "DISTINCT ") +
499+
sqlJoin(c.Args, ", ") +
500+
strOpt(len(c.Args) > 0 && len(c.NamedArgs) > 0, ", ") +
501+
sqlJoin(c.NamedArgs, ", ") +
502+
")"
488503
}
489504

490505
func (n *NamedArg) SQL() string { return n.Name.SQL() + " => " + n.Value.SQL() }
@@ -595,11 +610,7 @@ func (i *Ident) SQL() string {
595610
}
596611

597612
func (p *Path) SQL() string {
598-
sql := p.Idents[0].SQL()
599-
for _, id := range p.Idents[1:] {
600-
sql += "." + id.SQL()
601-
}
602-
return sql
613+
return sqlJoin(p.Idents, ".")
603614
}
604615

605616
func (a *ArrayLiteral) SQL() string {

‎go.mod‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/cloudspannerecosystem/memefish
22

3-
go 1.19
3+
go 1.20
44

55
require (
66
github.com/MakeNowJust/heredoc/v2 v2.0.1

0 commit comments

Comments
 (0)