diff --git a/.gitmodules b/.gitmodules index ed6a3d93..29ce44a6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "docs/themes/hugo-coder"] - path = docs/themes/hugo-coder - url = https://github.com/luizdepra/hugo-coder.git +[submodule "docs/themes/hugo-whisper-theme"] + path = docs/themes/hugo-whisper-theme + url = https://github.com/jugglerx/hugo-whisper-theme.git diff --git a/Makefile b/Makefile index 35027cb6..517e982c 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,14 @@ lint: bin/golangci-lint @echo bin/golangci-lint run ./... +.PHONY: docs +docs: + @echo + @echo " (x x) < memefish: docs" + @echo " /|||\\" + @echo + cd docs && hugo + .PHONY: ci ci: lint test diff --git a/README.md b/README.md index 6206c5df..b0e8464f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ +--- +title: README +--- +

- +

# méméfish @@ -18,134 +22,6 @@ - Generate Spanner SQL from AST (unparse) - Check expression type and semantics in SQL statement -Try it! - -```console -$ go run ./tools/analyze -param ./tools/param.yml 'select 1 + @foo' -+-------+ -| FOO | -+-------+ -| INT64 | -+-------+ - -$ go run ./tools/analyze -param ./tools/param.yml 'select @bar + 1 as bar' -analyze error::1:8: operator + requires two INT64/FLOAT64, but: STRUCT, INT64 - - 1: select @bar + 1 as bar - ^~~~~~~~ - -exit status 1 -``` - -## Example - -### Parse + Unparse - -```go -package main - -import ( - "fmt" - "log" - - "github.com/MakeNowJust/memefish/pkg/parser" - "github.com/MakeNowJust/memefish/pkg/token" - "github.com/k0kubun/pp" -) - -func main() { - // Create a new Parser instance. - file := &token.File{ - Buffer: "SELECT * FROM customers", - } - p := &parser.Parser{ - Lexer: &parser.Lexer{File: file}, - } - - // Do parsing! - stmt, err := p.ParseQuery() - if err != nil { - log.Fatal(err) - } - - // Show AST. - log.Print("AST") - _, _ = pp.Println(stmt) - - // Unparse AST to SQL source string. - log.Print("Unparse") - fmt.Println(stmt.SQL()) -} -``` - -### Analyze - -```go -package main - -import ( - "fmt" - "log" - - "github.com/MakeNowJust/memefish/pkg/analyzer" - "github.com/MakeNowJust/memefish/pkg/parser" - "github.com/MakeNowJust/memefish/pkg/token" -) - -func main() { - // Create a new Parser instance. - file := &token.File{ - Buffer: "SELECT * FROM singers", - } - p := &parser.Parser{ - Lexer: &parser.Lexer{File: file}, - } - - // Do parsing! - stmt, err := p.ParseQuery() - if err != nil { - log.Fatal(err) - } - - // Create table catalog. - catalog := &analyzer.Catalog{ - Tables: map[string]*analyzer.TableSchema{ - "SINGERS": { - Name: "Singers", - Columns: []*analyzer.ColumnSchema{ - {Name: "SingerId", Type: analyzer.Int64Type}, - {Name: "FirstName", Type: analyzer.StringType}, - {Name: "LastName", Type: analyzer.StringType}, - }, - }, - }, - } - - // Create a new Analyzer instance. - a := &analyzer.Analyzer{ - File: file, - Catalog: catalog, - } - - // Analyze! - err = a.AnalyzeQueryStatement(stmt) - if err != nil { - log.Fatal(err) - } - - // Get first column information. - columns := a.NameLists[stmt.Query] - fmt.Printf("1st column name : %s\n", columns[0].Text) - fmt.Printf("1st column type : %s\n", columns[0].Type) - fmt.Printf("1st column schema: %#v\n", columns[0].Deref().ColumnSchema) // == catalog.Tables["SINGERS"].Columns[0] -} -``` - -## TODO - -- Make more compatibility -- Build Spanner emulator on memefish - ## Notice This project is originally developed under "Expert team Go Engineer (Backend)" of [Mercari Summer Internship for Engineer 2019](https://mercan.mercari.com/articles/13497/). @@ -154,7 +30,5 @@ This project is originally developed under "Expert team Go Engineer (Backend)" o This project is licensed under MIT license. -2019 (C) TSUYUSATO "MakeNowJust" Kitsune - [godoc-badge]: https://img.shields.io/badge/godoc-reference-black.svg?style=for-the-badge&colorA=%235272B4&logo=go&logoColor=white [codecov-badge]: https://img.shields.io/codecov/c/github/MakeNowJust/memefish/master.svg?style=for-the-badge&colorA=FF005E&logo=codecov&logoColor=white diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..e54c19ca --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +resources/ +public/ \ No newline at end of file diff --git a/docs/archetypes/default.md b/docs/archetypes/default.md deleted file mode 100644 index 00e77bd7..00000000 --- a/docs/archetypes/default.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: "{{ replace .Name "-" " " | title }}" -date: {{ .Date }} -draft: true ---- - diff --git a/docs/config.toml b/docs/config.toml index e4b74182..f54e7c85 100644 --- a/docs/config.toml +++ b/docs/config.toml @@ -1,3 +1,41 @@ -baseURL = "http://example.org/" -languageCode = "en-us" -title = "My New Hugo Site" +baseurl = "https://makenowjust.github.io/memefish" +title = "méméfish" +theme = "hugo-whisper-theme" +languagecode = "en" +defaultcontentlanguage = "en" + +paginate = 20 +canonifyurls = true + +pygmentsstyle = "bw" +pygmentscodefences = true +pygmentscodefencesguesssyntax = true + +summaryLength = 30 + +[params] + homepage_button_link = "/docs" + homepage_button_text = "Read The Docs" + homepage_intro = "the foundation to analyze Spanner SQL" + homepage_image = "/images/terminal.svg" + + [params.logo] + mobile = "/images/memefish.svg" + standard = "/images/memefish.svg" + +[[menu.main]] + name = "Docs" + weight = 1 + url = "/docs/" +[[menu.main]] + name = "Demo" + weight = 2 + url = "/demo/" +[[menu.main]] + name = "GoDoc" + weight = 3 + url = "https://godoc.org/github.com/MakeNowJust/memefish/pkg" +[[menu.main]] + name = "GitHub" + weight = 3 + url = "https://github.com/MakeNowJust/memefish" \ No newline at end of file diff --git a/docs/content/docs/_index.md b/docs/content/docs/_index.md new file mode 120000 index 00000000..8a33348c --- /dev/null +++ b/docs/content/docs/_index.md @@ -0,0 +1 @@ +../../../README.md \ No newline at end of file diff --git a/docs/content/docs/example-analyze/index.md b/docs/content/docs/example-analyze/index.md new file mode 100644 index 00000000..830100ab --- /dev/null +++ b/docs/content/docs/example-analyze/index.md @@ -0,0 +1,76 @@ +--- +date: 2020-02-03 00:00:00 +0900 +title: "Example: analyze" +weight: 2 +--- + +This example shows how to analyze a Spanner SQL after parsing. + + + +## Code + +```go +package main + +import ( + "fmt" + "log" + + "github.com/MakeNowJust/memefish/pkg/analyzer" + "github.com/MakeNowJust/memefish/pkg/parser" + "github.com/MakeNowJust/memefish/pkg/token" +) + +func main() { + // Create a new Parser instance. + file := &token.File{ + Buffer: "SELECT * FROM singers", + } + p := &parser.Parser{ + Lexer: &parser.Lexer{File: file}, + } + + // Do parsing! + stmt, err := p.ParseQuery() + if err != nil { + log.Fatal(err) + } + + // Create table catalog. + catalog := &analyzer.Catalog{ + Tables: map[string]*analyzer.TableSchema{ + "SINGERS": { + Name: "Singers", + Columns: []*analyzer.ColumnSchema{ + {Name: "SingerId", Type: analyzer.Int64Type}, + {Name: "FirstName", Type: analyzer.StringType}, + {Name: "LastName", Type: analyzer.StringType}, + }, + }, + }, + } + + // Create a new Analyzer instance. + a := &analyzer.Analyzer{ + File: file, + Catalog: catalog, + } + + // Analyze! + err = a.AnalyzeQueryStatement(stmt) + if err != nil { + log.Fatal(err) + } + + // Get first column information. + columns := a.NameLists[stmt.Query] + fmt.Printf("1st column name : %s\n", columns[0].Text) + fmt.Printf("1st column type : %s\n", columns[0].Type) + fmt.Printf("1st column schema: %#v\n", columns[0].Deref().ColumnSchema) // == catalog.Tables["SINGERS"].Columns[0] +} +``` + +## Links + +- [analyzer - GoDoc](https://godoc.org/github.com/MakeNowJust/memefish/pkg/analyzer) \ No newline at end of file diff --git a/docs/content/docs/example-parse/index.md b/docs/content/docs/example-parse/index.md new file mode 100644 index 00000000..08eca419 --- /dev/null +++ b/docs/content/docs/example-parse/index.md @@ -0,0 +1,54 @@ +--- +date: 2020-02-03 00:00:00 +0900 +title: "Example: parse and unparse" +weight: 1 +--- + +This example shows how to parse a Spanner SQL and unparse it. + + + + ## Code + +```go +package main + +import ( + "fmt" + "log" + + "github.com/MakeNowJust/memefish/pkg/parser" + "github.com/MakeNowJust/memefish/pkg/token" + "github.com/k0kubun/pp" +) + +func main() { + // Create a new Parser instance. + file := &token.File{ + Buffer: "SELECT * FROM customers", + } + p := &parser.Parser{ + Lexer: &parser.Lexer{File: file}, + } + + // Do parsing! + stmt, err := p.ParseQuery() + if err != nil { + log.Fatal(err) + } + + // Show AST. + log.Print("AST") + _, _ = pp.Println(stmt) + + // Unparse AST to SQL source string. + log.Print("Unparse") + fmt.Println(stmt.SQL()) +} +``` + +## Links + +- [ast - GoDoc](https://godoc.org/github.com/MakeNowJust/memefish/pkg/ast) +- [parser - GoDoc](https://godoc.org/github.com/MakeNowJust/memefish/pkg/parser) +- [Query Syntax |  Cloud Spanner  |  Google Cloud](https://cloud.google.com/spanner/docs/query-syntax) \ No newline at end of file diff --git a/docs/layouts/_default/list.html b/docs/layouts/_default/list.html new file mode 100644 index 00000000..b23d3759 --- /dev/null +++ b/docs/layouts/_default/list.html @@ -0,0 +1,12 @@ +{{ define "header_css" }}{{ end }} +{{ define "body_classes" }}page-default-list{{ end }} +{{ define "header_classes" }}{{ end }} + +{{ define "main" }} + +

{{ .Title }}

+
+ {{ .Content }} +
+ +{{ end }} diff --git a/docs/layouts/partials/sub-footer.html b/docs/layouts/partials/sub-footer.html new file mode 100644 index 00000000..cd7b499c --- /dev/null +++ b/docs/layouts/partials/sub-footer.html @@ -0,0 +1,16 @@ + + \ No newline at end of file diff --git a/docs/static/images/memefish.svg b/docs/static/images/memefish.svg new file mode 120000 index 00000000..9ac175a8 --- /dev/null +++ b/docs/static/images/memefish.svg @@ -0,0 +1 @@ +../../../images/memefish.svg \ No newline at end of file diff --git a/docs/static/images/terminal.svg b/docs/static/images/terminal.svg new file mode 120000 index 00000000..ae07bd85 --- /dev/null +++ b/docs/static/images/terminal.svg @@ -0,0 +1 @@ +../../../images/terminal.svg \ No newline at end of file diff --git a/docs/themes/hugo-whisper-theme b/docs/themes/hugo-whisper-theme new file mode 160000 index 00000000..60c6443b --- /dev/null +++ b/docs/themes/hugo-whisper-theme @@ -0,0 +1 @@ +Subproject commit 60c6443b36efd49071f2ee70e7289ce9348ceb1c diff --git a/icon/memefish.png b/images/memefish.png similarity index 100% rename from icon/memefish.png rename to images/memefish.png diff --git a/icon/memefish.svg b/images/memefish.svg similarity index 100% rename from icon/memefish.svg rename to images/memefish.svg diff --git a/images/terminal.svg b/images/terminal.svg new file mode 100644 index 00000000..68d79620 --- /dev/null +++ b/images/terminal.svg @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + $ $ $ # $ # $ # S $ # Si $ # Sim $ # Simp $ # Simpl $ # Simple $ # Simple $ # Simple S $ # Simple SE $ # Simple SEL $ # Simple SELE $ # Simple SELEC $ # Simple SELECT $ # Simple SELECT $ # Simple SELECT q $ # Simple SELECT qu $ # Simple SELECT que $ # Simple SELECT quer $ # Simple SELECT query $ # Simple SELECT query $ g $ go $ go $ go r $ go ru $ go run $ go run $ go run . $ go run ./ $ go run ./t $ go run ./to $ go run ./too $ go run ./tools $ go run ./tools/ $ go run ./tools/a $ go run ./tools/an $ go run ./tools/analyze/ $ go run ./tools/analyze $ go run ./tools/analyze ' $ go run ./tools/analyze 'S $ go run ./tools/analyze 'SE $ go run ./tools/analyze 'SEL $ go run ./tools/analyze 'SELE $ go run ./tools/analyze 'SELEC $ go run ./tools/analyze 'SELECT $ go run ./tools/analyze 'SELECT $ go run ./tools/analyze 'SELECT * $ go run ./tools/analyze 'SELECT * $ go run ./tools/analyze 'SELECT * F $ go run ./tools/analyze 'SELECT * FR $ go run ./tools/analyze 'SELECT * FRO $ go run ./tools/analyze 'SELECT * FROM $ go run ./tools/analyze 'SELECT * FROM $ go run ./tools/analyze 'SELECT * FROM a $ go run ./tools/analyze 'SELECT * FROM al $ go run ./tools/analyze 'SELECT * FROM alb $ go run ./tools/analyze 'SELECT * FROM albu $ go run ./tools/analyze 'SELECT * FROM album $ go run ./tools/analyze 'SELECT * FROM albums $ go run ./tools/analyze 'SELECT * FROM albums' $ go run ./tools/analyze 'SELECT * FROM albums' +----------+---------+------------+-----------------+| SINGERID | ALBUMID | ALBUMTITLE | MARKETINGBUDGET || INT64 | INT64 | STRING | INT64 |$ c $ cl $ cle $ clea $ clear $ clear $ # P $ # Pa $ # Par $ # Para $ # Param $ # Parame $ # Paramet $ # Paramete $ # Parameter $ # Parameters $ # Parameters $ go run ./tools/analyze 'SELECT @ $ go run ./tools/analyze 'SELECT @f $ go run ./tools/analyze 'SELECT @fo $ go run ./tools/analyze 'SELECT @foo $ go run ./tools/analyze 'SELECT @foo $ go run ./tools/analyze 'SELECT @foo + $ go run ./tools/analyze 'SELECT @foo + $ go run ./tools/analyze 'SELECT @foo + 1 $ go run ./tools/analyze 'SELECT @foo + 1 $ go run ./tools/analyze 'SELECT @foo + 1 a $ go run ./tools/analyze 'SELECT @foo + 1 as $ go run ./tools/analyze 'SELECT @foo + 1 as $ go run ./tools/analyze 'SELECT @foo + 1 as f $ go run ./tools/analyze 'SELECT @foo + 1 as fo $ go run ./tools/analyze 'SELECT @foo + 1 as foo $ go run ./tools/analyze 'SELECT @foo + 1 as foo, $ go run ./tools/analyze 'SELECT @foo + 1 as foo, $ go run ./tools/analyze 'SELECT @foo + 1 as foo, @ $ go run ./tools/analyze 'SELECT @foo + 1 as foo, @b $ go run ./tools/analyze 'SELECT @foo + 1 as foo, @ba $ go run ./tools/analyze 'SELECT @foo + 1 as foo, @bar $ go run ./tools/analyze 'SELECT @foo + 1 as foo, @bar. $ go run ./tools/analyze 'SELECT @foo + 1 as foo, @bar.x $ go run ./tools/analyze 'SELECT @foo + 1 as foo, @bar.* $ go run ./tools/analyze 'SELECT @foo + 1 as foo, @bar.*' $ go run ./tools/analyze 'SELECT @foo + 1 as foo, @bar.*' +-------+-------+---------+ | FOO | X | Y | | INT64 | INT64 | FLOAT64 | $ # E $ # Er $ # Err $ # Erro $ # Error $ # Error $ # Error m $ # Error me $ # Error mes $ # Error mess $ # Error messa $ # Error messag $ # Error message $ # Error message $ go run ./tools/analyze 'SELECT @b $ go run ./tools/analyze 'SELECT @ba $ go run ./tools/analyze 'SELECT @bar $ go run ./tools/analyze 'SELECT @bar $ go run ./tools/analyze 'SELECT @bar + $ go run ./tools/analyze 'SELECT @bar + $ go run ./tools/analyze 'SELECT @bar + 1 $ go run ./tools/analyze 'SELECT @bar + 1 $ go run ./tools/analyze 'SELECT @bar + 1 a $ go run ./tools/analyze 'SELECT @bar + 1 as $ go run ./tools/analyze 'SELECT @bar + 1 as $ go run ./tools/analyze 'SELECT @bar + 1 as b $ go run ./tools/analyze 'SELECT @bar + 1 as ba $ go run ./tools/analyze 'SELECT @bar + 1 as bar $ go run ./tools/analyze 'SELECT @bar + 1 as bar' $ go run ./tools/analyze 'SELECT @bar + 1 as bar' analyze error::1:8: operator + requires two INT64/FLOAT64, but: STRUCT<x INT64, y FLOAT64>, INT64 1: SELECT @bar + 1 as bar ^~~~~~~~ exit status 1$ e $ ex $ exi $ exit $ exit + \ No newline at end of file diff --git a/tools/analyze/main.go b/tools/analyze/main.go index 69f792d1..6cf9377a 100644 --- a/tools/analyze/main.go +++ b/tools/analyze/main.go @@ -2,6 +2,7 @@ package main import ( "flag" + "fmt" "io/ioutil" "log" "os" @@ -15,9 +16,10 @@ import ( "gopkg.in/yaml.v2" ) -var param = flag.String("param", "", "param file") -var schema = flag.String("schema", "", "schema file") +var param = flag.String("param", "./tools/param.yml", "param file") +var schema = flag.String("schema", "./tools/schema.yml", "schema file") var debug = flag.Bool("debug", false, "enable debug") +var logging = flag.Bool("logging", false, "enable log") func init() { flag.Parse() @@ -25,14 +27,14 @@ func init() { func main() { if flag.NArg() < 1 { - log.Fatal("usage: ./analyze [SQL query]") + log.Fatal("usage: ./analyze [-param FILE] [-schema FILE] [-debug] [-logging] ") } query := flag.Arg(0) var params map[string]interface{} if *param != "" { - log.Printf("load param file: %s", *param) + logf("load param file: %s", *param) var err error params, err = loadParamFile(*param) if err != nil { @@ -42,7 +44,7 @@ func main() { var catalog map[string]*analyzer.TableSchema if *schema != "" { - log.Printf("load schema file: %s", *schema) + logf("load schema file: %s", *schema) var err error catalog, err = loadSchemaFile(*schema) if err != nil { @@ -50,7 +52,7 @@ func main() { } } - log.Printf("query: %q", query) + logf("query: %q", query) p := &parser.Parser{ Lexer: &parser.Lexer{ @@ -58,14 +60,15 @@ func main() { }, } - log.Printf("start parsing") + logf("start parsing") stmt, err := p.ParseQuery() if err != nil { - log.Fatal(err) + fmt.Fprintf(os.Stderr, "%v", err) + os.Exit(1) } - log.Printf("finish parsing successfully") + logf("finish parsing successfully") - log.Printf("start analyzing") + logf("start analyzing") a := &analyzer.Analyzer{ File: p.File, Params: params, @@ -73,9 +76,10 @@ func main() { } err = a.AnalyzeQueryStatement(stmt) if err != nil { - log.Fatal(err) + fmt.Fprintf(os.Stderr, "%v", err) + os.Exit(1) } - log.Printf("finish analyzing") + logf("finish analyzing") list := a.NameLists[stmt.Query] if list == nil { @@ -203,3 +207,9 @@ func decodeTableSchema(t *TableSchema) *analyzer.TableSchema { Columns: columns, } } + +func logf(msg string, params ...interface{}) { + if *logging { + log.Printf(msg, params...) + } +} diff --git a/tools/parse/main.go b/tools/parse/main.go index 2b8acdb9..6757654d 100644 --- a/tools/parse/main.go +++ b/tools/parse/main.go @@ -12,6 +12,7 @@ import ( ) var mode = flag.String("mode", "statement", "parsing mode") +var logging = flag.Bool("logging", false, "enable log") func init() { flag.Parse() @@ -19,12 +20,12 @@ func init() { func main() { if flag.NArg() < 1 { - log.Fatal("usage: ./parse [-mode statement|query|expr|ddl|dml] [SQL query]") + log.Fatal("usage: ./parse [-mode statement|query|expr|ddl|dml] [-logging] ") } query := flag.Arg(0) - log.Printf("query: %q", query) + logf("query: %q", query) p := &parser.Parser{ Lexer: &parser.Lexer{ @@ -32,7 +33,7 @@ func main() { }, } - log.Printf("start parsing") + logf("start parsing") var node ast.Node var err error switch *mode { @@ -50,7 +51,7 @@ func main() { if err != nil { log.Fatal(err) } - log.Printf("finish parsing successfully") + logf("finish parsing successfully") fmt.Println("--- AST") _, _ = pp.Println(node) @@ -58,3 +59,9 @@ func main() { fmt.Println("--- SQL") fmt.Println(node.SQL()) } + +func logf(msg string, params ...interface{}) { + if *logging { + log.Printf(msg, params...) + } +}