From 97a2cc4d5ed297c53d778cd288cd54eed980e5f1 Mon Sep 17 00:00:00 2001 From: yaacov Date: Mon, 17 Feb 2020 12:47:31 +0200 Subject: [PATCH] add v4 --- Makefile | 2 +- README.md | 56 +- TSL.g4 | 4 +- v5/cmd/README.md | 7 + v5/cmd/model/model.go | 42 + v5/cmd/tsl_gorm/main.go | 126 + v5/cmd/tsl_gorm/model.go | 68 + v5/cmd/tsl_graphql/main.go | 224 ++ v5/cmd/tsl_graphql/model.go | 71 + v5/cmd/tsl_mem/main.go | 90 + v5/cmd/tsl_mem/model.go | 83 + v5/cmd/tsl_mongo/main.go | 121 + v5/cmd/tsl_mongo/model.go | 48 + v5/cmd/tsl_parser/main.go | 91 + v5/cmd/tsl_sqlite/main.go | 125 + v5/cmd/tsl_sqlite/model.go | 71 + v5/go.mod | 50 + v5/pkg/parser/TSL.interp | 84 + v5/pkg/parser/TSL.tokens | 47 + v5/pkg/parser/TSLLexer.interp | 134 ++ v5/pkg/parser/TSLLexer.tokens | 47 + v5/pkg/parser/tsl_base_listener.go | 202 ++ v5/pkg/parser/tsl_lexer.go | 277 +++ v5/pkg/parser/tsl_listener.go | 190 ++ v5/pkg/parser/tsl_parser.go | 3167 +++++++++++++++++++++++++ v5/pkg/tsl/consts.go | 67 + v5/pkg/tsl/doc.go | 38 + v5/pkg/tsl/error_listener.go | 42 + v5/pkg/tsl/errors.go | 57 + v5/pkg/tsl/listener.go | 346 +++ v5/pkg/tsl/node.go | 23 + v5/pkg/tsl/tsl.go | 57 + v5/pkg/tsl/tsl_test.go | 215 ++ v5/pkg/walkers/README.md | 27 + v5/pkg/walkers/doc.go | 45 + v5/pkg/walkers/graphviz/walk.go | 153 ++ v5/pkg/walkers/ident/walk.go | 97 + v5/pkg/walkers/mongo/walk.go | 146 ++ v5/pkg/walkers/semantics/walk.go | 380 +++ v5/pkg/walkers/semantics/walk_test.go | 70 + v5/pkg/walkers/sql/doc_test.go | 49 + v5/pkg/walkers/sql/ops.go | 113 + v5/pkg/walkers/sql/walk.go | 194 ++ v5/pkg/walkers/sql/walk_test.go | 65 + 44 files changed, 7580 insertions(+), 31 deletions(-) create mode 100644 v5/cmd/README.md create mode 100644 v5/cmd/model/model.go create mode 100644 v5/cmd/tsl_gorm/main.go create mode 100644 v5/cmd/tsl_gorm/model.go create mode 100644 v5/cmd/tsl_graphql/main.go create mode 100644 v5/cmd/tsl_graphql/model.go create mode 100644 v5/cmd/tsl_mem/main.go create mode 100644 v5/cmd/tsl_mem/model.go create mode 100644 v5/cmd/tsl_mongo/main.go create mode 100644 v5/cmd/tsl_mongo/model.go create mode 100644 v5/cmd/tsl_parser/main.go create mode 100644 v5/cmd/tsl_sqlite/main.go create mode 100644 v5/cmd/tsl_sqlite/model.go create mode 100644 v5/go.mod create mode 100644 v5/pkg/parser/TSL.interp create mode 100644 v5/pkg/parser/TSL.tokens create mode 100644 v5/pkg/parser/TSLLexer.interp create mode 100644 v5/pkg/parser/TSLLexer.tokens create mode 100644 v5/pkg/parser/tsl_base_listener.go create mode 100644 v5/pkg/parser/tsl_lexer.go create mode 100644 v5/pkg/parser/tsl_listener.go create mode 100644 v5/pkg/parser/tsl_parser.go create mode 100644 v5/pkg/tsl/consts.go create mode 100644 v5/pkg/tsl/doc.go create mode 100644 v5/pkg/tsl/error_listener.go create mode 100644 v5/pkg/tsl/errors.go create mode 100644 v5/pkg/tsl/listener.go create mode 100644 v5/pkg/tsl/node.go create mode 100644 v5/pkg/tsl/tsl.go create mode 100644 v5/pkg/tsl/tsl_test.go create mode 100644 v5/pkg/walkers/README.md create mode 100644 v5/pkg/walkers/doc.go create mode 100644 v5/pkg/walkers/graphviz/walk.go create mode 100644 v5/pkg/walkers/ident/walk.go create mode 100644 v5/pkg/walkers/mongo/walk.go create mode 100644 v5/pkg/walkers/semantics/walk.go create mode 100644 v5/pkg/walkers/semantics/walk_test.go create mode 100644 v5/pkg/walkers/sql/doc_test.go create mode 100644 v5/pkg/walkers/sql/ops.go create mode 100644 v5/pkg/walkers/sql/walk.go create mode 100644 v5/pkg/walkers/sql/walk_test.go diff --git a/Makefile b/Makefile index aafe68d..43fc998 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ # # Major version directory -major:=v4 +major:=v5 # Details of the version of 'antlr' that will be downloaded to process the # grammar and generate the parser: diff --git a/README.md b/README.md index 627a532..c28c606 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ Tree Search Language (TSL) is a wonderful human readable filtering language. -[![Go Report Card](https://goreportcard.com/badge/github.com/yaacov/tree-search-language/v4)](https://goreportcard.com/report/github.com/yaacov/tree-search-language/v4) +[![Go Report Card](https://goreportcard.com/badge/github.com/yaacov/tree-search-language/v5)](https://goreportcard.com/report/github.com/yaacov/tree-search-language/v5) [![Build Status](https://travis-ci.org/yaacov/tree-search-language.svg?branch=master)](https://travis-ci.org/yaacov/tree-search-language) -[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/yaacov/tree-search-language/v4/pkg/tsl?tab=doc) +[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/tsl?tab=doc) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) The TSL language grammar is human readable and similar to SQL syntax. @@ -30,9 +30,9 @@ The TSL language grammar is human readable and similar to SQL syntax. You can use the TSL package to add uniform and powerful filtering to your RESTful API or GraphQL services, implement brewing-recipe searches on your smart tea brewer, or even make your own memory based "SQL like" server as we do in our `tsl_mem` CLI example. -([more examples](/v4/cmd/)) +([more examples](/v5/cmd/)) -Here is our `tsl_mem`CLI tool ([code](/v4/cmd/tsl_mem)), it's an in-memory search engine, it is using the TSL package to filter through an in-memory array of books using "SQL like" `tsl phrases`: +Here is our `tsl_mem`CLI tool ([code](/v5/cmd/tsl_mem)), it's an in-memory search engine, it is using the TSL package to filter through an in-memory array of books using "SQL like" `tsl phrases`: ``` bash $ ./tsl_mem -i "spec.rating is not null and author ~= 'Joe'" -o prettyjson @@ -127,16 +127,16 @@ go get -u github.com/onsi/ginkgo/ginkgo ``` bash # Install the base package -go get "github.com/yaacov/tree-search-language/v4/pkg/tsl" +go get "github.com/yaacov/tree-search-language/v5/pkg/tsl" # Install all walkers -go get "github.com/yaacov/tree-search-language/v4/pkg/walkers/..." +go get "github.com/yaacov/tree-search-language/v5/pkg/walkers/..." # Or pick the walker needed -go get "github.com/yaacov/tree-search-language/v4/pkg/walkers/sql" -go get "github.com/yaacov/tree-search-language/v4/pkg/walkers/mongo" -go get "github.com/yaacov/tree-search-language/v4/pkg/walkers/ident" -go get "github.com/yaacov/tree-search-language/v4/pkg/walkers/graphviz" +go get "github.com/yaacov/tree-search-language/v5/pkg/walkers/sql" +go get "github.com/yaacov/tree-search-language/v5/pkg/walkers/mongo" +go get "github.com/yaacov/tree-search-language/v5/pkg/walkers/ident" +go get "github.com/yaacov/tree-search-language/v5/pkg/walkers/graphviz" ``` #### Installing the command line examples using `go get` @@ -144,11 +144,11 @@ go get "github.com/yaacov/tree-search-language/v4/pkg/walkers/graphviz" See CLI tools usage [here](https://github.com/yaacov/tree-search-language#cli-tools). ``` bash -go get -v "github.com/yaacov/tree-search-language/v4/cmd/tsl_parser" -go get -v "github.com/yaacov/tree-search-language/v4/cmd/tsl_mongo" -go get -v "github.com/yaacov/tree-search-language/v4/cmd/tsl_sqlite" -go get -v "github.com/yaacov/tree-search-language/v4/cmd/tsl_gorm" -go get -v "github.com/yaacov/tree-search-language/v4/cmd/tsl_graphql" +go get -v "github.com/yaacov/tree-search-language/v5/cmd/tsl_parser" +go get -v "github.com/yaacov/tree-search-language/v5/cmd/tsl_mongo" +go get -v "github.com/yaacov/tree-search-language/v5/cmd/tsl_sqlite" +go get -v "github.com/yaacov/tree-search-language/v5/cmd/tsl_gorm" +go get -v "github.com/yaacov/tree-search-language/v5/cmd/tsl_graphql" ``` ## Syntax examples @@ -202,12 +202,12 @@ dot file.dot -Tpng > image.png ## Code examples -For complete working code examples, see the CLI tools [directory](/v4/cmd) +For complete working code examples, see the CLI tools [directory](/v5/cmd) ( see more on TSL's CLI tools usage [here](https://github.com/yaacov/tree-search-language#cli-tools) ). ##### ParseTSL -The `tsl` package include the ParseTSL [code](/v4/pkg/tsl/tsl.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v4/pkg/tsl#ParseTSL) method for parsing TSL into a search tree: +The `tsl` package include the ParseTSL [code](/v5/pkg/tsl/tsl.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/tsl#ParseTSL) method for parsing TSL into a search tree: ``` go tree, err := tsl.ParseTSL("name in ('joe', 'jane') and grade not between 0 and 50") ``` @@ -218,13 +218,13 @@ After parsing the TSL tree will look like this (image created using the `tsl_par ##### sql.Walk -The `walkers` `sql` package include a helper sql.Walk ([code](/v4/pkg/walkers/sql/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v4/pkg/walkers/sql#Walk)) method that adds search to [squirrel](https://github.com/Masterminds/squirrel)'s SelectBuilder object: +The `walkers` `sql` package include a helper sql.Walk ([code](/v5/pkg/walkers/sql/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/sql#Walk)) method that adds search to [squirrel](https://github.com/Masterminds/squirrel)'s SelectBuilder object: ``` go import ( ... sq "github.com/Masterminds/squirrel" - "github.com/yaacov/tree-search-language/v4/pkg/walkers/sql" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/sql" ... ) @@ -253,12 +253,12 @@ SELECT name, city, state FROM users WHERE (name IN (?,?) AND grade NOT BETWEEN ? ##### mongo.Walk -The `walkers` `mongo` package include a helper mongo.Walk ([code](/v4/pkg/walkers/mongo/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v4/pkg/walkers/mongo#Walk)) method that adds search bson filter to [mongo-go-driver](https://github.com/mongodb/mongo-go-driver): +The `walkers` `mongo` package include a helper mongo.Walk ([code](/v5/pkg/walkers/mongo/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/mongo#Walk)) method that adds search bson filter to [mongo-go-driver](https://github.com/mongodb/mongo-go-driver): ``` go import ( ... - "github.com/yaacov/tree-search-language/v4/pkg/walkers/mongo" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/mongo" ... ) @@ -274,12 +274,12 @@ cur, err := collection.Find(ctx, filter) ##### graphviz.Walk -The `walkers` `graphviz` package include a helper graphviz.Walk ([code](/v4/pkg/walkers/graphviz/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v4/pkg/walkers/graphviz#Walk)) method that exports `.dot` file nodes : +The `walkers` `graphviz` package include a helper graphviz.Walk ([code](/v5/pkg/walkers/graphviz/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/graphviz#Walk)) method that exports `.dot` file nodes : ``` go import ( ... - "github.com/yaacov/tree-search-language/v4/pkg/walkers/graphviz" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/graphviz" ... ) @@ -295,12 +295,12 @@ s = fmt.Sprintf("digraph {\n%s\n}\n", s) ##### ident.Walk -The `walkers` `ident` package include a helper ident.Walk ([code](/v4/pkg/walkers/ident/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v4/pkg/walkers/ident#Walk)) method that checks and mapps identifier names: +The `walkers` `ident` package include a helper ident.Walk ([code](/v5/pkg/walkers/ident/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/ident#Walk)) method that checks and mapps identifier names: ``` go import ( ... - "github.com/yaacov/tree-search-language/v4/pkg/walkers/ident" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/ident" ... ) ... @@ -336,12 +336,12 @@ tree, err = ident.Walk(tree, checkColumnName) ##### semantics.Walk -The `walkers` `semantics` package include a helper semantics.Walk ([code](/v4/pkg/walkers/semantics/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v4/pkg/walkers/semantics#Walk)) method that helps filter a list of objects using a `tsl tree`, and a `type` `semantics.EvalFunc` ([code](/v4/pkg/walkers/semantics/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v4/pkg/walkers/semantics#EvalFunc)) that return a record's value for a record key: +The `walkers` `semantics` package include a helper semantics.Walk ([code](/v5/pkg/walkers/semantics/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/semantics#Walk)) method that helps filter a list of objects using a `tsl tree`, and a `type` `semantics.EvalFunc` ([code](/v5/pkg/walkers/semantics/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/semantics#EvalFunc)) that return a record's value for a record key: ``` go import ( ... - "github.com/yaacov/tree-search-language/v4/pkg/walkers/semantics" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/semantics" ... ) ... @@ -379,7 +379,7 @@ compliance, err = semantics.Walk(tree, eval) ## CLI tools -The example CLI tools showcase the TSL language and `tsl` golang package, see the [cmd](/v4/cmd) directory for code. +The example CLI tools showcase the TSL language and `tsl` golang package, see the [cmd](/v5/cmd) directory for code. ##### tsl_parser diff --git a/TSL.g4 b/TSL.g4 index 1b525f5..d2b4e4c 100644 --- a/TSL.g4 +++ b/TSL.g4 @@ -40,7 +40,7 @@ tableName ; columnName - : ( ( databaseName '.' )? tableName '.' )? IDENTIFIER + : IDENTIFIER ; literalValue @@ -85,7 +85,7 @@ IDENTIFIER : '"' (~'"' | '""')* '"' | '`' (~'`' | '``')* '`' | '[' ~']'* ']' - | [a-zA-Z_] [a-zA-Z_0-9]* // TODO check: needs more chars in set + | [a-zA-Z_] [a-zA-Z_./0-9]* [a-zA-Z_0-9]* // TODO check: needs more chars in set ; NUMERIC_LITERAL diff --git a/v5/cmd/README.md b/v5/cmd/README.md new file mode 100644 index 0000000..7aeea2f --- /dev/null +++ b/v5/cmd/README.md @@ -0,0 +1,7 @@ + + +

+ TSL Logo +

+ +# Tree Search Language (TSL) Command Line Interface (CLI) Examples diff --git a/v5/cmd/model/model.go b/v5/cmd/model/model.go new file mode 100644 index 0000000..e21ef44 --- /dev/null +++ b/v5/cmd/model/model.go @@ -0,0 +1,42 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package model for demo. +package model + +// BookSpecs for demo. +type BookSpecs struct { + Pages uint `bson:"pages,omitempty" json:"pages,omitempty"` + Rating uint `bson:"rating,omitempty" json:"rating,omitempty"` +} + +// Book for demo. +type Book struct { + Title string `bson:"title,omitempty" json:"title,omitempty"` + Author string `bson:"author,omitempty" json:"author,omitempty"` + Spec BookSpecs `bson:"spec,omitempty" json:"spec,omitempty"` +} + +// Books are the demo list of books. +var Books = []interface{}{ + Book{Title: "Book", Author: "Joe", Spec: BookSpecs{Pages: 100, Rating: 4}}, + Book{Title: "Other Book", Author: "Jane", Spec: BookSpecs{Pages: 200, Rating: 3}}, + Book{Title: "Some Book", Author: "Jane", Spec: BookSpecs{Pages: 50, Rating: 5}}, + Book{Title: "Some Other Book", Author: "Jane", Spec: BookSpecs{Pages: 50}}, + Book{Title: "Good Book", Author: "Joe", Spec: BookSpecs{Pages: 150, Rating: 4}}, + Book{Title: "Some Great Book", Author: "Jane", Spec: BookSpecs{Pages: 550, Rating: 2}}, + Book{Title: "Other Great Book", Author: "Jane", Spec: BookSpecs{Pages: 250}}, + Book{Title: "My Big Book", Author: "Joe", Spec: BookSpecs{Pages: 15, Rating: 5}}, +} diff --git a/v5/cmd/tsl_gorm/main.go b/v5/cmd/tsl_gorm/main.go new file mode 100644 index 0000000..6791988 --- /dev/null +++ b/v5/cmd/tsl_gorm/main.go @@ -0,0 +1,126 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package main. +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "log" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/ident" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/sql" + + "github.com/yaacov/tree-search-language/v5/cmd/model" +) + +func check(err error) { + if err != nil { + log.Fatal(err) + } +} + +// columnNamesMap mapps between user namespace and the SQL column names. +var columnNamesMap = map[string]string{ + "title": "title", + "author": "author", + "spec.pages": "pages", + "spec.rating": "rating", +} + +// checkColumnName checks if a coulumn name is valid in user space replace it +// with the mapped column name and returns and error if not a valid name. +func checkColumnName(s string) (string, error) { + // Chekc for column name in map. + if v, ok := columnNamesMap[s]; ok { + return v, nil + } + + // If not found return string as is, and an error. + return s, fmt.Errorf("column \"%s\" not found", s) +} + +func main() { + // Setup the input. + inputPtr := flag.String("i", "title is not null", "the tsl string to parse (e.g. \"title = 'Book'\")") + preparePtr := flag.Bool("p", false, "prepare a book collection for queries") + filePtr := flag.String("f", "./sqlite.db", "the sqlite database file name") + flag.Parse() + + // Set context. + ctx := context.Background() + + // Try to connect to sqlite driver. + tx, err := connect(ctx, *filePtr) + check(err) + defer tx.Commit() + + // Create a clean books collection. + if *preparePtr { + err = prepareCollection(tx) + check(err) + } + + // Parse input string into a TSL tree. + tree, err := tsl.ParseTSL(*inputPtr) + check(err) + + // Check and replace user identifiers with the SQL table column names. + tree, err = ident.Walk(tree, checkColumnName) + check(err) + + // Prepare SQL filter. + filter, err := sql.Walk(tree) + check(err) + + // Create SQL query. + sql, args, err := filter.ToSql() + check(err) + + // Query SQL table. + rows, err := tx.Model(&Book{}).Where(sql, args...).Select("*").Rows() + check(err) + defer rows.Close() + + for rows.Next() { + var elem Book + + // ScanRows scan a row into our gorm Book element. + err = tx.ScanRows(rows, &elem) + check(err) + + // Conver gorm Book type to model Book type. + book := model.Book{ + Title: elem.Title, + Author: elem.Author, + Spec: model.BookSpecs{ + Pages: elem.Pages, + Rating: elem.Rating, + }, + } + + // Print out one Book in json format. + b, _ := json.Marshal(book) + fmt.Printf("%s\n", string(b)) + } + + // Check for errors and exit. + err = rows.Err() + check(err) +} diff --git a/v5/cmd/tsl_gorm/model.go b/v5/cmd/tsl_gorm/model.go new file mode 100644 index 0000000..90cf38d --- /dev/null +++ b/v5/cmd/tsl_gorm/model.go @@ -0,0 +1,68 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/sqlite" + + "github.com/yaacov/tree-search-language/v5/cmd/model" +) + +// Book represent a book model. +type Book struct { + gorm.Model + Title string + Author string + Pages uint + Rating uint +} + +func connect(ctx context.Context, url string) (tx *gorm.DB, err error) { + db, err := gorm.Open("sqlite3", url) + check(err) + + // Migrate the schema. + db.AutoMigrate(&Book{}) + + // Begin transaction. + tx = db.Begin() + + return +} + +func prepareCollection(tx *gorm.DB) (err error) { + // Delete all books in the DB. + tx.Where("title is not null").Delete(Book{}) + + // Insert new books into DB. + for _, b := range model.Books { + tx.Create(&Book{ + Title: b.(model.Book).Title, + Author: b.(model.Book).Author, + Pages: b.(model.Book).Spec.Pages, + Rating: b.(model.Book).Spec.Rating, + }) + + if tx.Error != nil { + return + } + } + + return +} diff --git a/v5/cmd/tsl_graphql/main.go b/v5/cmd/tsl_graphql/main.go new file mode 100644 index 0000000..4f3a3d6 --- /dev/null +++ b/v5/cmd/tsl_graphql/main.go @@ -0,0 +1,224 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package main. +package main + +import ( + "context" + "database/sql" + "encoding/json" + "flag" + "fmt" + "log" + "net/http" + + sq "github.com/Masterminds/squirrel" + "github.com/graphql-go/graphql" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/ident" + walker "github.com/yaacov/tree-search-language/v5/pkg/walkers/sql" + + "github.com/yaacov/tree-search-language/v5/cmd/model" +) + +var preparePtr *bool +var filePtr *string + +// columnNamesMap mapps between user namespace and the SQL column names. +var columnNamesMap = map[string]string{ + "title": "title", + "author": "author", + "spec.pages": "pages", + "spec.rating": "rating", +} + +var bookSpecType = graphql.NewObject(graphql.ObjectConfig{ + Name: "BookSpecs", + Fields: graphql.Fields{ + "pages": &graphql.Field{ + Type: graphql.Int, + }, + "rating": &graphql.Field{ + Type: graphql.Int, + }, + }, +}) + +var bookType = graphql.NewObject(graphql.ObjectConfig{ + Name: "Book", + Fields: graphql.Fields{ + "title": &graphql.Field{ + Type: graphql.String, + }, + "author": &graphql.Field{ + Type: graphql.String, + }, + "spec": &graphql.Field{ + Type: bookSpecType, + }, + }, +}) + +func check(err error) { + if err != nil { + log.Fatal(err) + } +} + +// checkColumnName checks if a coulumn name is valid in user space replace it +// with the mapped column name and returns and error if not a valid name. +func checkColumnName(s string) (string, error) { + // Chekc for column name in map. + if v, ok := columnNamesMap[s]; ok { + return v, nil + } + + // If not found return string as is, and an error. + return s, fmt.Errorf("column \"%s\" not found", s) +} + +func sqlQuery(ctx context.Context, filter string) (books []model.Book, err error) { + var bookID uint + var rows *sql.Rows + + // Try to connect to sqlite driver. + tx, err := connect(ctx, *filePtr) + if err != nil { + return nil, err + } + defer tx.Commit() + + // Parse input string into a TSL tree. + tree, err := tsl.ParseTSL(filter) + if err != nil { + return nil, err + } + + // Check and replace user identifiers with the SQL table column names. + tree, err = ident.Walk(tree, checkColumnName) + if err != nil { + return nil, err + } + + // Prepare SQL filter. + sqFilter, err := walker.Walk(tree) + if err != nil { + return nil, err + } + + // Query SQL table. + rows, err = sq. + Select("*"). + Where(sqFilter). + From("books"). + RunWith(tx). + QueryContext(ctx) + if err != nil { + return nil, err + } + + for rows.Next() { + elem := model.Book{} + err = rows.Scan( + &bookID, + &elem.Title, + &elem.Author, + &elem.Spec.Pages, + &elem.Spec.Rating, + ) + if err != nil { + return nil, err + } + + books = append(books, elem) + } + + // Check for errors and exit. + err = rows.Err() + if err != nil { + return nil, err + } + + return +} + +func main() { + // Setup the input. + preparePtr = flag.Bool("p", false, "prepare a book collection for queries") + filePtr = flag.String("f", "./sqlite.db", "the sqlite database file name") + flag.Parse() + + // Set context. + ctx := context.Background() + + // Create a clean books collection. + if *preparePtr { + tx, err := connect(ctx, *filePtr) + check(err) + + err = prepareCollection(ctx, tx) + check(err) + + tx.Commit() + } + + rootQuery := graphql.NewObject(graphql.ObjectConfig{ + Name: "Query", + Fields: graphql.Fields{ + "books": &graphql.Field{ + Type: graphql.NewList(bookType), + Description: "list books", + Args: graphql.FieldConfigArgument{ + "filter": &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.String), + Description: "use a TSL phrase to filter results", + }, + }, + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + filter := params.Args["filter"].(string) + return sqlQuery(ctx, filter) + }, + }, + }, + }) + + schema, _ := graphql.NewSchema(graphql.SchemaConfig{ + Query: rootQuery, + }) + + qraphqlHandler := func(w http.ResponseWriter, r *http.Request) { + result := graphql.Do(graphql.Params{ + Schema: schema, + RequestString: r.URL.Query().Get("query"), + }) + + // Write output to response. + json.NewEncoder(w).Encode(result) + } + + http.HandleFunc("/graphql", qraphqlHandler) + + msg := ` +TSL GraphQL server listen on port: 8080 + +Query example: + curl -sG "http://localhost:8080/graphql" --data-urlencode \ + "query={books(filter:\"title like '%%Other%%' and spec.pages>100\"){title,author,spec{pages}}}"` + + fmt.Println(msg) + http.ListenAndServe(":8080", nil) +} diff --git a/v5/cmd/tsl_graphql/model.go b/v5/cmd/tsl_graphql/model.go new file mode 100644 index 0000000..a07c580 --- /dev/null +++ b/v5/cmd/tsl_graphql/model.go @@ -0,0 +1,71 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "database/sql" + + _ "github.com/mattn/go-sqlite3" + + "github.com/yaacov/tree-search-language/v5/cmd/model" +) + +const sqlStmt = ` +create table if not exists books ( + id integer not null primary key, + title text, + author text, + pages integer, + rating integer +); +delete from books; +` + +func connect(ctx context.Context, url string) (tx *sql.Tx, err error) { + var db *sql.DB + + db, err = sql.Open("sqlite3", url) + check(err) + + tx, err = db.BeginTx(ctx, nil) + + return +} + +func prepareCollection(ctx context.Context, tx *sql.Tx) (err error) { + // Create table. + _, err = tx.ExecContext(ctx, sqlStmt) + check(err) + + // Insert new books into the table. + stmt, err := tx.PrepareContext(ctx, "insert into books(title, author, pages, rating) values(?, ?, ?, ?)") + check(err) + + defer stmt.Close() + + for _, b := range model.Books { + _, err = stmt.ExecContext(ctx, + b.(model.Book).Title, + b.(model.Book).Author, + b.(model.Book).Spec.Pages, + b.(model.Book).Spec.Rating) + + check(err) + } + + return +} diff --git a/v5/cmd/tsl_mem/main.go b/v5/cmd/tsl_mem/main.go new file mode 100644 index 0000000..4b61101 --- /dev/null +++ b/v5/cmd/tsl_mem/main.go @@ -0,0 +1,90 @@ +// Copyright 2019 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: 2019 Nimrod Shneor +// Author: 2019 Yaacov Zamir + +// Package main. +package main + +import ( + "encoding/json" + "flag" + "fmt" + "log" + + prettyjson "github.com/hokaccha/go-prettyjson" + "github.com/yaacov/tree-search-language/v5/pkg/tsl" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/ident" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/semantics" + yaml "gopkg.in/yaml.v2" +) + +func check(err error) { + if err != nil { + log.Fatal(err) + } +} + +func main() { + var s []byte + books := []Book{} + + // Setup the input. + inputPtr := flag.String("i", "", "the tsl string to parse (e.g. \"author = 'Joe'\")") + outputPtr := flag.String("o", "json", "output format [json/yaml/prettyjson]") + flag.Parse() + + // Sanity check. + if *inputPtr == "" { + log.Fatal("missing required flag -i (the tsl string to parse)") + } + + // Parse input string into a TSL tree. + tree, err := tsl.ParseTSL(*inputPtr) + check(err) + + // Check and replace user identifiers with the document field names. + tree, err = ident.Walk(tree, checkColumnName) + check(err) + + // Prepare the books in memeory collection. + err = prepareCollection() + check(err) + + // Filter the books collection using our stl tree. + for _, book := range Books { + matchingFilter, err := semantics.Walk(tree, evalFactory(book)) + check(err) + if matchingFilter { + books = append(books, book) + } + } + + // Printout the filtered list. + switch *outputPtr { + case "json": + s, err = json.Marshal(books) + case "yaml": + s, err = yaml.Marshal(books) + case "prettyjson": + s, err = prettyjson.Marshal(books) + default: + err = fmt.Errorf("unsuported output format: %s", *outputPtr) + } + + check(err) + fmt.Printf("%s\n", s) +} diff --git a/v5/cmd/tsl_mem/model.go b/v5/cmd/tsl_mem/model.go new file mode 100644 index 0000000..7605cce --- /dev/null +++ b/v5/cmd/tsl_mem/model.go @@ -0,0 +1,83 @@ +// Copyright 2019 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: 2019 Nimrod Shneor +// Author: 2019 Yaacov Zamir + +package main + +import ( + "fmt" + + "github.com/yaacov/tree-search-language/v5/pkg/walkers/semantics" + + "github.com/yaacov/tree-search-language/v5/cmd/model" +) + +// Book represent one book in our in-memmory data base. +type Book map[string]interface{} + +// Books are the demo list of books. +var Books = []Book{} + +// columnNamesMap mapps between user namespace and the document field names. +var columnNamesMap = map[string]string{ + "title": "title", + "author": "author", + "spec.pages": "spec.pages", + "spec.rating": "spec.rating", +} + +// checkColumnName checks if a coulumn name is valid in user space replace it +// with the mapped column name and returns and error if not a valid name. +func checkColumnName(s string) (string, error) { + // Chekc for column name in map. + if v, ok := columnNamesMap[s]; ok { + return v, nil + } + + // If not found return string as is, and an error. + return s, fmt.Errorf("column \"%s\" not found", s) +} + +func evalFactory(book Book) semantics.EvalFunc { + return func(k string) (interface{}, bool) { + return book[k], true + } +} + +func prepareCollection() (err error) { + // Insert new books into the table. + for _, b := range model.Books { + // Create a new book. + newBook := Book{ + "title": b.(model.Book).Title, + "author": b.(model.Book).Author, + } + + // Add optional parameters. + if b.(model.Book).Spec.Pages > 0 { + newBook["spec.pages"] = b.(model.Book).Spec.Pages + } + if b.(model.Book).Spec.Rating > 0 { + newBook["spec.rating"] = b.(model.Book).Spec.Rating + } + + // Insert new book to the books arra. + Books = append(Books, newBook) + } + + return +} diff --git a/v5/cmd/tsl_mongo/main.go b/v5/cmd/tsl_mongo/main.go new file mode 100644 index 0000000..aeba791 --- /dev/null +++ b/v5/cmd/tsl_mongo/main.go @@ -0,0 +1,121 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package main. +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "log" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/ident" + walker "github.com/yaacov/tree-search-language/v5/pkg/walkers/mongo" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + + "github.com/yaacov/tree-search-language/v5/cmd/model" +) + +func check(err error) { + if err != nil { + log.Fatal(err) + } +} + +// columnNamesMap mapps between user namespace and the MongoDB document fields. +var columnNamesMap = map[string]string{ + "title": "title", + "author": "author", + "spec.pages": "spec.pages", + "spec.rating": "spec.rating", +} + +// checkColumnName checks if a coulumn name is valid in user space replace it +// with the mapped column name and returns and error if not a valid name. +func checkColumnName(s string) (string, error) { + // Chekc for column name in map. + if v, ok := columnNamesMap[s]; ok { + return v, nil + } + + // If not found return string as is, and an error. + return s, fmt.Errorf("column \"%s\" not found", s) +} + +func main() { + var err error + var client *mongo.Client + var collection *mongo.Collection + var filter bson.D + + // Setup the input. + inputPtr := flag.String("i", "title is not null", "the tsl string to parse (e.g. \"author = 'Jane'\")") + preparePtr := flag.Bool("p", false, "prepare a book collection for queries") + dbNamePtr := flag.String("d", "tsl", "db name to connect to") + collectionNamePtr := flag.String("c", "books", "collection name to query on") + urlPtr := flag.String("u", "mongodb://localhost:27017", "url for mongo server") + flag.Parse() + + // Set context. + ctx := context.Background() + + // Try to connect to mongo server. + client, err = connect(ctx, *urlPtr) + check(err) + + // Set up a collection. + collection = client.Database(*dbNamePtr).Collection(*collectionNamePtr) + + // Create a clean books collection. + if *preparePtr { + err = prepareCollection(ctx, collection) + check(err) + } + + // Parse input string into a TSL tree. + tree, err := tsl.ParseTSL(*inputPtr) + check(err) + + // Check and replace user identifiers with MongoDB document field names. + tree, err = ident.Walk(tree, checkColumnName) + check(err) + + // Prepare a bson filter. + filter, err = walker.Walk(tree) + check(err) + + // Run query. + cur, err := collection.Find(ctx, filter) + check(err) + defer cur.Close(ctx) + + // Loop on query elements. + for cur.Next(ctx) { + elem := model.Book{} + err := cur.Decode(&elem) + check(err) + + b, _ := json.Marshal(elem) + fmt.Printf("%s\n", string(b)) + } + + // Check for errors and exit. + err = cur.Err() + check(err) +} diff --git a/v5/cmd/tsl_mongo/model.go b/v5/cmd/tsl_mongo/model.go new file mode 100644 index 0000000..ac4b77e --- /dev/null +++ b/v5/cmd/tsl_mongo/model.go @@ -0,0 +1,48 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + + "github.com/yaacov/tree-search-language/v5/cmd/model" +) + +func connect(ctx context.Context, url string) (client *mongo.Client, err error) { + client, err = mongo.NewClient(options.Client().ApplyURI(url)) + if err != nil { + return + } + err = client.Connect(ctx) + + return +} + +func prepareCollection(ctx context.Context, collection *mongo.Collection) (err error) { + // Drop the collection. + err = collection.Drop(ctx) + if err != nil { + return + } + + // Insert new books into the collection. + _, err = collection.InsertMany(ctx, model.Books) + + return +} diff --git a/v5/cmd/tsl_parser/main.go b/v5/cmd/tsl_parser/main.go new file mode 100644 index 0000000..5d8059b --- /dev/null +++ b/v5/cmd/tsl_parser/main.go @@ -0,0 +1,91 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package main. +package main + +import ( + "encoding/json" + "flag" + "fmt" + "log" + + sq "github.com/Masterminds/squirrel" + "github.com/hokaccha/go-prettyjson" + "gopkg.in/yaml.v2" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/graphviz" + walker "github.com/yaacov/tree-search-language/v5/pkg/walkers/sql" +) + +func check(err error) { + if err != nil { + log.Fatal(err) + } +} + +func main() { + var s []byte + var st string + + // Setup the input. + inputPtr := flag.String("i", "", "the tsl string to parse (e.g. \"title = 'kitty'\")") + outputPtr := flag.String("o", "json", "output format [json/yaml/prettyjson/sql]") + flag.Parse() + + // Sanity check. + if *inputPtr == "" { + log.Fatal("missing required flag -i (the tsl string to parse)") + } + + // Parse input string into a TSL tree. + tree, err := tsl.ParseTSL(*inputPtr) + check(err) + + switch *outputPtr { + case "json": + s, err = json.Marshal(tree) + case "yaml": + s, err = yaml.Marshal(tree) + case "prettyjson": + s, err = prettyjson.Marshal(tree) + case "dot": + st, err = graphviz.Walk("", tree, "root") + s = append(s, fmt.Sprintf("digraph {\n%s\n}\n", st)...) + case "sql": + var sql string + var args []interface{} + var filter sq.Sqlizer + + // Use Squirrel to walk the tree, and create SQL filter. + filter, err = walker.Walk(tree) + + // Add SQL template to bytes slice. + if err == nil { + sql, args, err = sq. + Select("*"). + Where(filter). + From("table_name"). + ToSql() + + s = append(s, fmt.Sprintf("sql: %s\n", sql)...) + s = append(s, fmt.Sprintf("args: %v", args)...) + } + } + + check(err) + fmt.Println(string(s)) +} diff --git a/v5/cmd/tsl_sqlite/main.go b/v5/cmd/tsl_sqlite/main.go new file mode 100644 index 0000000..3600aee --- /dev/null +++ b/v5/cmd/tsl_sqlite/main.go @@ -0,0 +1,125 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package main. +package main + +import ( + "context" + "database/sql" + "encoding/json" + "flag" + "fmt" + "log" + + sq "github.com/Masterminds/squirrel" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" + "github.com/yaacov/tree-search-language/v5/pkg/walkers/ident" + walker "github.com/yaacov/tree-search-language/v5/pkg/walkers/sql" + + "github.com/yaacov/tree-search-language/v5/cmd/model" +) + +func check(err error) { + if err != nil { + log.Fatal(err) + } +} + +// columnNamesMap mapps between user namespace and the SQL column names. +var columnNamesMap = map[string]string{ + "title": "title", + "author": "author", + "spec.pages": "pages", + "spec.rating": "rating", +} + +// checkColumnName checks if a coulumn name is valid in user space replace it +// with the mapped column name and returns and error if not a valid name. +func checkColumnName(s string) (string, error) { + // Chekc for column name in map. + if v, ok := columnNamesMap[s]; ok { + return v, nil + } + + // If not found return string as is, and an error. + return s, fmt.Errorf("column \"%s\" not found", s) +} + +func main() { + var bookID uint + var rows *sql.Rows + + // Setup the input. + inputPtr := flag.String("i", "title is not null", "the tsl string to parse (e.g. \"title = 'Book'\")") + preparePtr := flag.Bool("p", false, "prepare a book collection for queries") + filePtr := flag.String("f", "./sqlite.db", "the sqlite database file name") + flag.Parse() + + // Set context. + ctx := context.Background() + + // Try to connect to sqlite driver. + tx, err := connect(ctx, *filePtr) + check(err) + defer tx.Commit() + + // Create a clean books collection. + if *preparePtr { + err = prepareCollection(ctx, tx) + check(err) + } + + // Parse input string into a TSL tree. + tree, err := tsl.ParseTSL(*inputPtr) + check(err) + + // Check and replace user identifiers with the SQL table column names. + tree, err = ident.Walk(tree, checkColumnName) + check(err) + + // Prepare SQL filter. + filter, err := walker.Walk(tree) + check(err) + + // Query SQL table. + rows, err = sq. + Select("*"). + Where(filter). + From("books"). + RunWith(tx). + QueryContext(ctx) + check(err) + + for rows.Next() { + elem := model.Book{} + err = rows.Scan( + &bookID, + &elem.Title, + &elem.Author, + &elem.Spec.Pages, + &elem.Spec.Rating, + ) + check(err) + + b, _ := json.Marshal(elem) + fmt.Printf("%s\n", string(b)) + } + + // Check for errors and exit. + err = rows.Err() + check(err) +} diff --git a/v5/cmd/tsl_sqlite/model.go b/v5/cmd/tsl_sqlite/model.go new file mode 100644 index 0000000..a07c580 --- /dev/null +++ b/v5/cmd/tsl_sqlite/model.go @@ -0,0 +1,71 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "database/sql" + + _ "github.com/mattn/go-sqlite3" + + "github.com/yaacov/tree-search-language/v5/cmd/model" +) + +const sqlStmt = ` +create table if not exists books ( + id integer not null primary key, + title text, + author text, + pages integer, + rating integer +); +delete from books; +` + +func connect(ctx context.Context, url string) (tx *sql.Tx, err error) { + var db *sql.DB + + db, err = sql.Open("sqlite3", url) + check(err) + + tx, err = db.BeginTx(ctx, nil) + + return +} + +func prepareCollection(ctx context.Context, tx *sql.Tx) (err error) { + // Create table. + _, err = tx.ExecContext(ctx, sqlStmt) + check(err) + + // Insert new books into the table. + stmt, err := tx.PrepareContext(ctx, "insert into books(title, author, pages, rating) values(?, ?, ?, ?)") + check(err) + + defer stmt.Close() + + for _, b := range model.Books { + _, err = stmt.ExecContext(ctx, + b.(model.Book).Title, + b.(model.Book).Author, + b.(model.Book).Spec.Pages, + b.(model.Book).Spec.Rating) + + check(err) + } + + return +} diff --git a/v5/go.mod b/v5/go.mod new file mode 100644 index 0000000..70e6edc --- /dev/null +++ b/v5/go.mod @@ -0,0 +1,50 @@ +module github.com/yaacov/tree-search-language/v5 + +require ( + cloud.google.com/go v0.39.0 // indirect + github.com/Masterminds/squirrel v1.1.0 + github.com/antlr/antlr4 v0.0.0-20190518164840-edae2a1c9b4b + github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 // indirect + github.com/gogo/protobuf v1.3.0 // indirect + github.com/golang/mock v1.3.1 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d // indirect + github.com/golangci/golangci-lint v1.19.1 // indirect + github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 // indirect + github.com/google/pprof v0.0.0-20190515194954-54271f7e092f // indirect + github.com/gostaticanalysis/analysisutil v0.0.3 // indirect + github.com/graphql-go/graphql v0.7.8 + github.com/hokaccha/go-prettyjson v0.0.0-20180920040306-f579f869bbfe + github.com/jinzhu/gorm v1.9.8 + github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/magiconair/properties v1.8.1 // indirect + github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb // indirect + github.com/mattn/go-isatty v0.0.9 // indirect + github.com/mattn/go-sqlite3 v1.10.0 + github.com/onsi/ginkgo v1.10.1 + github.com/onsi/gomega v1.7.0 + github.com/pelletier/go-toml v1.4.0 // indirect + github.com/securego/gosec v0.0.0-20190924081645-29341f6e9c08 // indirect + github.com/spf13/afero v1.2.2 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/tidwall/pretty v0.0.0-20190325153808-1166b9ac2b65 // indirect + github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect + github.com/xdg/stringprep v1.0.0 // indirect + go.mongodb.org/mongo-driver v1.0.2 + go.opencensus.io v0.22.0 // indirect + golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 // indirect + golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff // indirect + golang.org/x/lint v0.0.0-20190409202823-959b441ac422 // indirect + golang.org/x/mobile v0.0.0-20190509164839-32b2708ab171 // indirect + golang.org/x/mod v0.1.0 // indirect + golang.org/x/oauth2 v0.0.0-20190523182746-aaccbc9213b0 // indirect + golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe // indirect + golang.org/x/tools v0.0.0-20190925020647-22afafe3322a // indirect + google.golang.org/appengine v1.6.0 // indirect + google.golang.org/genproto v0.0.0-20190530194941-fb225487d101 // indirect + gopkg.in/yaml.v2 v2.2.2 + mvdan.cc/unparam v0.0.0-20190917161559-b83a221c10a2 // indirect + sourcegraph.com/sqs/pbtypes v1.0.0 // indirect +) + +go 1.13 diff --git a/v5/pkg/parser/TSL.interp b/v5/pkg/parser/TSL.interp new file mode 100644 index 0000000..302b25e --- /dev/null +++ b/v5/pkg/parser/TSL.interp @@ -0,0 +1,84 @@ +token literal names: +null +'(' +',' +')' +'<' +'<=' +'>' +'>=' +'=' +'!=' +'<>' +'~=' +'~!' +'*' +'/' +'%' +'+' +'-' +null +null +null +null +null +null +null +null +null +null +null +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +K_LIKE +K_ILIKE +K_AND +K_OR +K_BETWEEN +K_IN +K_IS +K_NULL +K_NOT +IDENTIFIER +NUMERIC_LITERAL +STRING_LITERAL +SPACES + +rule names: +start +expr +literalOp +stringOp +likeOp +databaseName +tableName +columnName +literalValue +mathExp +signedNumber +stringValue +keyNot + + +atn: +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 32, 178, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 43, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 51, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 58, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 64, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 73, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 80, 10, 3, 12, 3, 14, 3, 83, 11, 3, 5, 3, 85, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 95, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 103, 10, 3, 12, 3, 14, 3, 106, 11, 3, 3, 4, 3, 4, 5, 4, 110, 10, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 5, 10, 124, 10, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 132, 10, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 138, 10, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 144, 10, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 150, 10, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 156, 10, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 162, 10, 11, 7, 11, 164, 10, 11, 12, 11, 14, 11, 167, 11, 11, 3, 12, 5, 12, 170, 10, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 2, 4, 4, 20, 15, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 2, 7, 3, 2, 6, 9, 3, 2, 10, 12, 3, 2, 13, 14, 3, 2, 20, 21, 3, 2, 18, 19, 2, 195, 2, 28, 3, 2, 2, 2, 4, 94, 3, 2, 2, 2, 6, 109, 3, 2, 2, 2, 8, 111, 3, 2, 2, 2, 10, 113, 3, 2, 2, 2, 12, 115, 3, 2, 2, 2, 14, 117, 3, 2, 2, 2, 16, 119, 3, 2, 2, 2, 18, 123, 3, 2, 2, 2, 20, 131, 3, 2, 2, 2, 22, 169, 3, 2, 2, 2, 24, 173, 3, 2, 2, 2, 26, 175, 3, 2, 2, 2, 28, 29, 5, 4, 3, 2, 29, 30, 7, 2, 2, 3, 30, 3, 3, 2, 2, 2, 31, 32, 8, 3, 1, 2, 32, 33, 5, 20, 11, 2, 33, 34, 5, 6, 4, 2, 34, 35, 5, 18, 10, 2, 35, 95, 3, 2, 2, 2, 36, 37, 5, 20, 11, 2, 37, 38, 5, 8, 5, 2, 38, 39, 5, 18, 10, 2, 39, 95, 3, 2, 2, 2, 40, 42, 5, 20, 11, 2, 41, 43, 5, 26, 14, 2, 42, 41, 3, 2, 2, 2, 42, 43, 3, 2, 2, 2, 43, 44, 3, 2, 2, 2, 44, 45, 5, 10, 6, 2, 45, 46, 5, 18, 10, 2, 46, 95, 3, 2, 2, 2, 47, 48, 5, 20, 11, 2, 48, 50, 7, 26, 2, 2, 49, 51, 5, 26, 14, 2, 50, 49, 3, 2, 2, 2, 50, 51, 3, 2, 2, 2, 51, 52, 3, 2, 2, 2, 52, 53, 7, 27, 2, 2, 53, 95, 3, 2, 2, 2, 54, 55, 5, 20, 11, 2, 55, 57, 7, 26, 2, 2, 56, 58, 5, 26, 14, 2, 57, 56, 3, 2, 2, 2, 57, 58, 3, 2, 2, 2, 58, 59, 3, 2, 2, 2, 59, 60, 5, 18, 10, 2, 60, 95, 3, 2, 2, 2, 61, 63, 5, 20, 11, 2, 62, 64, 5, 26, 14, 2, 63, 62, 3, 2, 2, 2, 63, 64, 3, 2, 2, 2, 64, 65, 3, 2, 2, 2, 65, 66, 7, 24, 2, 2, 66, 67, 5, 18, 10, 2, 67, 68, 7, 22, 2, 2, 68, 69, 5, 18, 10, 2, 69, 95, 3, 2, 2, 2, 70, 72, 5, 20, 11, 2, 71, 73, 5, 26, 14, 2, 72, 71, 3, 2, 2, 2, 72, 73, 3, 2, 2, 2, 73, 74, 3, 2, 2, 2, 74, 75, 7, 25, 2, 2, 75, 84, 7, 3, 2, 2, 76, 81, 5, 18, 10, 2, 77, 78, 7, 4, 2, 2, 78, 80, 5, 18, 10, 2, 79, 77, 3, 2, 2, 2, 80, 83, 3, 2, 2, 2, 81, 79, 3, 2, 2, 2, 81, 82, 3, 2, 2, 2, 82, 85, 3, 2, 2, 2, 83, 81, 3, 2, 2, 2, 84, 76, 3, 2, 2, 2, 84, 85, 3, 2, 2, 2, 85, 86, 3, 2, 2, 2, 86, 87, 7, 5, 2, 2, 87, 95, 3, 2, 2, 2, 88, 89, 7, 28, 2, 2, 89, 95, 5, 4, 3, 6, 90, 91, 7, 3, 2, 2, 91, 92, 5, 4, 3, 2, 92, 93, 7, 5, 2, 2, 93, 95, 3, 2, 2, 2, 94, 31, 3, 2, 2, 2, 94, 36, 3, 2, 2, 2, 94, 40, 3, 2, 2, 2, 94, 47, 3, 2, 2, 2, 94, 54, 3, 2, 2, 2, 94, 61, 3, 2, 2, 2, 94, 70, 3, 2, 2, 2, 94, 88, 3, 2, 2, 2, 94, 90, 3, 2, 2, 2, 95, 104, 3, 2, 2, 2, 96, 97, 12, 5, 2, 2, 97, 98, 7, 22, 2, 2, 98, 103, 5, 4, 3, 6, 99, 100, 12, 4, 2, 2, 100, 101, 7, 23, 2, 2, 101, 103, 5, 4, 3, 5, 102, 96, 3, 2, 2, 2, 102, 99, 3, 2, 2, 2, 103, 106, 3, 2, 2, 2, 104, 102, 3, 2, 2, 2, 104, 105, 3, 2, 2, 2, 105, 5, 3, 2, 2, 2, 106, 104, 3, 2, 2, 2, 107, 110, 9, 2, 2, 2, 108, 110, 9, 3, 2, 2, 109, 107, 3, 2, 2, 2, 109, 108, 3, 2, 2, 2, 110, 7, 3, 2, 2, 2, 111, 112, 9, 4, 2, 2, 112, 9, 3, 2, 2, 2, 113, 114, 9, 5, 2, 2, 114, 11, 3, 2, 2, 2, 115, 116, 7, 29, 2, 2, 116, 13, 3, 2, 2, 2, 117, 118, 7, 29, 2, 2, 118, 15, 3, 2, 2, 2, 119, 120, 7, 29, 2, 2, 120, 17, 3, 2, 2, 2, 121, 124, 5, 22, 12, 2, 122, 124, 5, 24, 13, 2, 123, 121, 3, 2, 2, 2, 123, 122, 3, 2, 2, 2, 124, 19, 3, 2, 2, 2, 125, 126, 8, 11, 1, 2, 126, 132, 5, 16, 9, 2, 127, 128, 7, 3, 2, 2, 128, 129, 5, 20, 11, 2, 129, 130, 7, 5, 2, 2, 130, 132, 3, 2, 2, 2, 131, 125, 3, 2, 2, 2, 131, 127, 3, 2, 2, 2, 132, 165, 3, 2, 2, 2, 133, 134, 12, 8, 2, 2, 134, 137, 7, 15, 2, 2, 135, 138, 5, 18, 10, 2, 136, 138, 5, 20, 11, 2, 137, 135, 3, 2, 2, 2, 137, 136, 3, 2, 2, 2, 138, 164, 3, 2, 2, 2, 139, 140, 12, 7, 2, 2, 140, 143, 7, 16, 2, 2, 141, 144, 5, 18, 10, 2, 142, 144, 5, 20, 11, 2, 143, 141, 3, 2, 2, 2, 143, 142, 3, 2, 2, 2, 144, 164, 3, 2, 2, 2, 145, 146, 12, 6, 2, 2, 146, 149, 7, 17, 2, 2, 147, 150, 5, 18, 10, 2, 148, 150, 5, 20, 11, 2, 149, 147, 3, 2, 2, 2, 149, 148, 3, 2, 2, 2, 150, 164, 3, 2, 2, 2, 151, 152, 12, 5, 2, 2, 152, 155, 7, 18, 2, 2, 153, 156, 5, 18, 10, 2, 154, 156, 5, 20, 11, 2, 155, 153, 3, 2, 2, 2, 155, 154, 3, 2, 2, 2, 156, 164, 3, 2, 2, 2, 157, 158, 12, 4, 2, 2, 158, 161, 7, 19, 2, 2, 159, 162, 5, 18, 10, 2, 160, 162, 5, 20, 11, 2, 161, 159, 3, 2, 2, 2, 161, 160, 3, 2, 2, 2, 162, 164, 3, 2, 2, 2, 163, 133, 3, 2, 2, 2, 163, 139, 3, 2, 2, 2, 163, 145, 3, 2, 2, 2, 163, 151, 3, 2, 2, 2, 163, 157, 3, 2, 2, 2, 164, 167, 3, 2, 2, 2, 165, 163, 3, 2, 2, 2, 165, 166, 3, 2, 2, 2, 166, 21, 3, 2, 2, 2, 167, 165, 3, 2, 2, 2, 168, 170, 9, 6, 2, 2, 169, 168, 3, 2, 2, 2, 169, 170, 3, 2, 2, 2, 170, 171, 3, 2, 2, 2, 171, 172, 7, 30, 2, 2, 172, 23, 3, 2, 2, 2, 173, 174, 7, 31, 2, 2, 174, 25, 3, 2, 2, 2, 175, 176, 7, 28, 2, 2, 176, 27, 3, 2, 2, 2, 23, 42, 50, 57, 63, 72, 81, 84, 94, 102, 104, 109, 123, 131, 137, 143, 149, 155, 161, 163, 165, 169] \ No newline at end of file diff --git a/v5/pkg/parser/TSL.tokens b/v5/pkg/parser/TSL.tokens new file mode 100644 index 0000000..02c61e1 --- /dev/null +++ b/v5/pkg/parser/TSL.tokens @@ -0,0 +1,47 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +T__13=14 +T__14=15 +T__15=16 +T__16=17 +K_LIKE=18 +K_ILIKE=19 +K_AND=20 +K_OR=21 +K_BETWEEN=22 +K_IN=23 +K_IS=24 +K_NULL=25 +K_NOT=26 +IDENTIFIER=27 +NUMERIC_LITERAL=28 +STRING_LITERAL=29 +SPACES=30 +'('=1 +','=2 +')'=3 +'<'=4 +'<='=5 +'>'=6 +'>='=7 +'='=8 +'!='=9 +'<>'=10 +'~='=11 +'~!'=12 +'*'=13 +'/'=14 +'%'=15 +'+'=16 +'-'=17 diff --git a/v5/pkg/parser/TSLLexer.interp b/v5/pkg/parser/TSLLexer.interp new file mode 100644 index 0000000..b4318f8 --- /dev/null +++ b/v5/pkg/parser/TSLLexer.interp @@ -0,0 +1,134 @@ +token literal names: +null +'(' +',' +')' +'<' +'<=' +'>' +'>=' +'=' +'!=' +'<>' +'~=' +'~!' +'*' +'/' +'%' +'+' +'-' +null +null +null +null +null +null +null +null +null +null +null +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +K_LIKE +K_ILIKE +K_AND +K_OR +K_BETWEEN +K_IN +K_IS +K_NULL +K_NOT +IDENTIFIER +NUMERIC_LITERAL +STRING_LITERAL +SPACES + +rule names: +T__0 +T__1 +T__2 +T__3 +T__4 +T__5 +T__6 +T__7 +T__8 +T__9 +T__10 +T__11 +T__12 +T__13 +T__14 +T__15 +T__16 +K_LIKE +K_ILIKE +K_AND +K_OR +K_BETWEEN +K_IN +K_IS +K_NULL +K_NOT +IDENTIFIER +NUMERIC_LITERAL +STRING_LITERAL +SPACES +DIGIT +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 32, 354, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 28, 7, 28, 203, 10, 28, 12, 28, 14, 28, 206, 11, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 7, 28, 213, 10, 28, 12, 28, 14, 28, 216, 11, 28, 3, 28, 3, 28, 3, 28, 7, 28, 221, 10, 28, 12, 28, 14, 28, 224, 11, 28, 3, 28, 3, 28, 3, 28, 7, 28, 229, 10, 28, 12, 28, 14, 28, 232, 11, 28, 3, 28, 7, 28, 235, 10, 28, 12, 28, 14, 28, 238, 11, 28, 5, 28, 240, 10, 28, 3, 29, 6, 29, 243, 10, 29, 13, 29, 14, 29, 244, 3, 29, 3, 29, 7, 29, 249, 10, 29, 12, 29, 14, 29, 252, 11, 29, 5, 29, 254, 10, 29, 3, 29, 3, 29, 5, 29, 258, 10, 29, 3, 29, 6, 29, 261, 10, 29, 13, 29, 14, 29, 262, 5, 29, 265, 10, 29, 3, 29, 3, 29, 6, 29, 269, 10, 29, 13, 29, 14, 29, 270, 3, 29, 3, 29, 5, 29, 275, 10, 29, 3, 29, 6, 29, 278, 10, 29, 13, 29, 14, 29, 279, 5, 29, 282, 10, 29, 5, 29, 284, 10, 29, 3, 30, 3, 30, 3, 30, 3, 30, 7, 30, 290, 10, 30, 12, 30, 14, 30, 293, 11, 30, 3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 33, 3, 33, 3, 34, 3, 34, 3, 35, 3, 35, 3, 36, 3, 36, 3, 37, 3, 37, 3, 38, 3, 38, 3, 39, 3, 39, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3, 43, 3, 44, 3, 44, 3, 45, 3, 45, 3, 46, 3, 46, 3, 47, 3, 47, 3, 48, 3, 48, 3, 49, 3, 49, 3, 50, 3, 50, 3, 51, 3, 51, 3, 52, 3, 52, 3, 53, 3, 53, 3, 54, 3, 54, 3, 55, 3, 55, 3, 56, 3, 56, 3, 57, 3, 57, 3, 58, 3, 58, 2, 2, 59, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 22, 43, 23, 45, 24, 47, 25, 49, 26, 51, 27, 53, 28, 55, 29, 57, 30, 59, 31, 61, 32, 63, 2, 65, 2, 67, 2, 69, 2, 71, 2, 73, 2, 75, 2, 77, 2, 79, 2, 81, 2, 83, 2, 85, 2, 87, 2, 89, 2, 91, 2, 93, 2, 95, 2, 97, 2, 99, 2, 101, 2, 103, 2, 105, 2, 107, 2, 109, 2, 111, 2, 113, 2, 115, 2, 3, 2, 38, 3, 2, 36, 36, 3, 2, 98, 98, 3, 2, 95, 95, 5, 2, 67, 92, 97, 97, 99, 124, 6, 2, 48, 59, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 4, 2, 45, 45, 47, 47, 3, 2, 41, 41, 5, 2, 11, 13, 15, 15, 34, 34, 3, 2, 50, 59, 4, 2, 67, 67, 99, 99, 4, 2, 68, 68, 100, 100, 4, 2, 69, 69, 101, 101, 4, 2, 70, 70, 102, 102, 4, 2, 71, 71, 103, 103, 4, 2, 72, 72, 104, 104, 4, 2, 73, 73, 105, 105, 4, 2, 74, 74, 106, 106, 4, 2, 75, 75, 107, 107, 4, 2, 76, 76, 108, 108, 4, 2, 77, 77, 109, 109, 4, 2, 78, 78, 110, 110, 4, 2, 79, 79, 111, 111, 4, 2, 80, 80, 112, 112, 4, 2, 81, 81, 113, 113, 4, 2, 82, 82, 114, 114, 4, 2, 83, 83, 115, 115, 4, 2, 84, 84, 116, 116, 4, 2, 85, 85, 117, 117, 4, 2, 86, 86, 118, 118, 4, 2, 87, 87, 119, 119, 4, 2, 88, 88, 120, 120, 4, 2, 89, 89, 121, 121, 4, 2, 90, 90, 122, 122, 4, 2, 91, 91, 123, 123, 4, 2, 92, 92, 124, 124, 2, 349, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 2, 49, 3, 2, 2, 2, 2, 51, 3, 2, 2, 2, 2, 53, 3, 2, 2, 2, 2, 55, 3, 2, 2, 2, 2, 57, 3, 2, 2, 2, 2, 59, 3, 2, 2, 2, 2, 61, 3, 2, 2, 2, 3, 117, 3, 2, 2, 2, 5, 119, 3, 2, 2, 2, 7, 121, 3, 2, 2, 2, 9, 123, 3, 2, 2, 2, 11, 125, 3, 2, 2, 2, 13, 128, 3, 2, 2, 2, 15, 130, 3, 2, 2, 2, 17, 133, 3, 2, 2, 2, 19, 135, 3, 2, 2, 2, 21, 138, 3, 2, 2, 2, 23, 141, 3, 2, 2, 2, 25, 144, 3, 2, 2, 2, 27, 147, 3, 2, 2, 2, 29, 149, 3, 2, 2, 2, 31, 151, 3, 2, 2, 2, 33, 153, 3, 2, 2, 2, 35, 155, 3, 2, 2, 2, 37, 157, 3, 2, 2, 2, 39, 162, 3, 2, 2, 2, 41, 168, 3, 2, 2, 2, 43, 172, 3, 2, 2, 2, 45, 175, 3, 2, 2, 2, 47, 183, 3, 2, 2, 2, 49, 186, 3, 2, 2, 2, 51, 189, 3, 2, 2, 2, 53, 194, 3, 2, 2, 2, 55, 239, 3, 2, 2, 2, 57, 283, 3, 2, 2, 2, 59, 285, 3, 2, 2, 2, 61, 296, 3, 2, 2, 2, 63, 300, 3, 2, 2, 2, 65, 302, 3, 2, 2, 2, 67, 304, 3, 2, 2, 2, 69, 306, 3, 2, 2, 2, 71, 308, 3, 2, 2, 2, 73, 310, 3, 2, 2, 2, 75, 312, 3, 2, 2, 2, 77, 314, 3, 2, 2, 2, 79, 316, 3, 2, 2, 2, 81, 318, 3, 2, 2, 2, 83, 320, 3, 2, 2, 2, 85, 322, 3, 2, 2, 2, 87, 324, 3, 2, 2, 2, 89, 326, 3, 2, 2, 2, 91, 328, 3, 2, 2, 2, 93, 330, 3, 2, 2, 2, 95, 332, 3, 2, 2, 2, 97, 334, 3, 2, 2, 2, 99, 336, 3, 2, 2, 2, 101, 338, 3, 2, 2, 2, 103, 340, 3, 2, 2, 2, 105, 342, 3, 2, 2, 2, 107, 344, 3, 2, 2, 2, 109, 346, 3, 2, 2, 2, 111, 348, 3, 2, 2, 2, 113, 350, 3, 2, 2, 2, 115, 352, 3, 2, 2, 2, 117, 118, 7, 42, 2, 2, 118, 4, 3, 2, 2, 2, 119, 120, 7, 46, 2, 2, 120, 6, 3, 2, 2, 2, 121, 122, 7, 43, 2, 2, 122, 8, 3, 2, 2, 2, 123, 124, 7, 62, 2, 2, 124, 10, 3, 2, 2, 2, 125, 126, 7, 62, 2, 2, 126, 127, 7, 63, 2, 2, 127, 12, 3, 2, 2, 2, 128, 129, 7, 64, 2, 2, 129, 14, 3, 2, 2, 2, 130, 131, 7, 64, 2, 2, 131, 132, 7, 63, 2, 2, 132, 16, 3, 2, 2, 2, 133, 134, 7, 63, 2, 2, 134, 18, 3, 2, 2, 2, 135, 136, 7, 35, 2, 2, 136, 137, 7, 63, 2, 2, 137, 20, 3, 2, 2, 2, 138, 139, 7, 62, 2, 2, 139, 140, 7, 64, 2, 2, 140, 22, 3, 2, 2, 2, 141, 142, 7, 128, 2, 2, 142, 143, 7, 63, 2, 2, 143, 24, 3, 2, 2, 2, 144, 145, 7, 128, 2, 2, 145, 146, 7, 35, 2, 2, 146, 26, 3, 2, 2, 2, 147, 148, 7, 44, 2, 2, 148, 28, 3, 2, 2, 2, 149, 150, 7, 49, 2, 2, 150, 30, 3, 2, 2, 2, 151, 152, 7, 39, 2, 2, 152, 32, 3, 2, 2, 2, 153, 154, 7, 45, 2, 2, 154, 34, 3, 2, 2, 2, 155, 156, 7, 47, 2, 2, 156, 36, 3, 2, 2, 2, 157, 158, 5, 87, 44, 2, 158, 159, 5, 81, 41, 2, 159, 160, 5, 85, 43, 2, 160, 161, 5, 73, 37, 2, 161, 38, 3, 2, 2, 2, 162, 163, 5, 81, 41, 2, 163, 164, 5, 87, 44, 2, 164, 165, 5, 81, 41, 2, 165, 166, 5, 85, 43, 2, 166, 167, 5, 73, 37, 2, 167, 40, 3, 2, 2, 2, 168, 169, 5, 65, 33, 2, 169, 170, 5, 91, 46, 2, 170, 171, 5, 71, 36, 2, 171, 42, 3, 2, 2, 2, 172, 173, 5, 93, 47, 2, 173, 174, 5, 99, 50, 2, 174, 44, 3, 2, 2, 2, 175, 176, 5, 67, 34, 2, 176, 177, 5, 73, 37, 2, 177, 178, 5, 103, 52, 2, 178, 179, 5, 109, 55, 2, 179, 180, 5, 73, 37, 2, 180, 181, 5, 73, 37, 2, 181, 182, 5, 91, 46, 2, 182, 46, 3, 2, 2, 2, 183, 184, 5, 81, 41, 2, 184, 185, 5, 91, 46, 2, 185, 48, 3, 2, 2, 2, 186, 187, 5, 81, 41, 2, 187, 188, 5, 101, 51, 2, 188, 50, 3, 2, 2, 2, 189, 190, 5, 91, 46, 2, 190, 191, 5, 105, 53, 2, 191, 192, 5, 87, 44, 2, 192, 193, 5, 87, 44, 2, 193, 52, 3, 2, 2, 2, 194, 195, 5, 91, 46, 2, 195, 196, 5, 93, 47, 2, 196, 197, 5, 103, 52, 2, 197, 54, 3, 2, 2, 2, 198, 204, 7, 36, 2, 2, 199, 203, 10, 2, 2, 2, 200, 201, 7, 36, 2, 2, 201, 203, 7, 36, 2, 2, 202, 199, 3, 2, 2, 2, 202, 200, 3, 2, 2, 2, 203, 206, 3, 2, 2, 2, 204, 202, 3, 2, 2, 2, 204, 205, 3, 2, 2, 2, 205, 207, 3, 2, 2, 2, 206, 204, 3, 2, 2, 2, 207, 240, 7, 36, 2, 2, 208, 214, 7, 98, 2, 2, 209, 213, 10, 3, 2, 2, 210, 211, 7, 98, 2, 2, 211, 213, 7, 98, 2, 2, 212, 209, 3, 2, 2, 2, 212, 210, 3, 2, 2, 2, 213, 216, 3, 2, 2, 2, 214, 212, 3, 2, 2, 2, 214, 215, 3, 2, 2, 2, 215, 217, 3, 2, 2, 2, 216, 214, 3, 2, 2, 2, 217, 240, 7, 98, 2, 2, 218, 222, 7, 93, 2, 2, 219, 221, 10, 4, 2, 2, 220, 219, 3, 2, 2, 2, 221, 224, 3, 2, 2, 2, 222, 220, 3, 2, 2, 2, 222, 223, 3, 2, 2, 2, 223, 225, 3, 2, 2, 2, 224, 222, 3, 2, 2, 2, 225, 240, 7, 95, 2, 2, 226, 230, 9, 5, 2, 2, 227, 229, 9, 6, 2, 2, 228, 227, 3, 2, 2, 2, 229, 232, 3, 2, 2, 2, 230, 228, 3, 2, 2, 2, 230, 231, 3, 2, 2, 2, 231, 236, 3, 2, 2, 2, 232, 230, 3, 2, 2, 2, 233, 235, 9, 7, 2, 2, 234, 233, 3, 2, 2, 2, 235, 238, 3, 2, 2, 2, 236, 234, 3, 2, 2, 2, 236, 237, 3, 2, 2, 2, 237, 240, 3, 2, 2, 2, 238, 236, 3, 2, 2, 2, 239, 198, 3, 2, 2, 2, 239, 208, 3, 2, 2, 2, 239, 218, 3, 2, 2, 2, 239, 226, 3, 2, 2, 2, 240, 56, 3, 2, 2, 2, 241, 243, 5, 63, 32, 2, 242, 241, 3, 2, 2, 2, 243, 244, 3, 2, 2, 2, 244, 242, 3, 2, 2, 2, 244, 245, 3, 2, 2, 2, 245, 253, 3, 2, 2, 2, 246, 250, 7, 48, 2, 2, 247, 249, 5, 63, 32, 2, 248, 247, 3, 2, 2, 2, 249, 252, 3, 2, 2, 2, 250, 248, 3, 2, 2, 2, 250, 251, 3, 2, 2, 2, 251, 254, 3, 2, 2, 2, 252, 250, 3, 2, 2, 2, 253, 246, 3, 2, 2, 2, 253, 254, 3, 2, 2, 2, 254, 264, 3, 2, 2, 2, 255, 257, 5, 73, 37, 2, 256, 258, 9, 8, 2, 2, 257, 256, 3, 2, 2, 2, 257, 258, 3, 2, 2, 2, 258, 260, 3, 2, 2, 2, 259, 261, 5, 63, 32, 2, 260, 259, 3, 2, 2, 2, 261, 262, 3, 2, 2, 2, 262, 260, 3, 2, 2, 2, 262, 263, 3, 2, 2, 2, 263, 265, 3, 2, 2, 2, 264, 255, 3, 2, 2, 2, 264, 265, 3, 2, 2, 2, 265, 284, 3, 2, 2, 2, 266, 268, 7, 48, 2, 2, 267, 269, 5, 63, 32, 2, 268, 267, 3, 2, 2, 2, 269, 270, 3, 2, 2, 2, 270, 268, 3, 2, 2, 2, 270, 271, 3, 2, 2, 2, 271, 281, 3, 2, 2, 2, 272, 274, 5, 73, 37, 2, 273, 275, 9, 8, 2, 2, 274, 273, 3, 2, 2, 2, 274, 275, 3, 2, 2, 2, 275, 277, 3, 2, 2, 2, 276, 278, 5, 63, 32, 2, 277, 276, 3, 2, 2, 2, 278, 279, 3, 2, 2, 2, 279, 277, 3, 2, 2, 2, 279, 280, 3, 2, 2, 2, 280, 282, 3, 2, 2, 2, 281, 272, 3, 2, 2, 2, 281, 282, 3, 2, 2, 2, 282, 284, 3, 2, 2, 2, 283, 242, 3, 2, 2, 2, 283, 266, 3, 2, 2, 2, 284, 58, 3, 2, 2, 2, 285, 291, 7, 41, 2, 2, 286, 290, 10, 9, 2, 2, 287, 288, 7, 41, 2, 2, 288, 290, 7, 41, 2, 2, 289, 286, 3, 2, 2, 2, 289, 287, 3, 2, 2, 2, 290, 293, 3, 2, 2, 2, 291, 289, 3, 2, 2, 2, 291, 292, 3, 2, 2, 2, 292, 294, 3, 2, 2, 2, 293, 291, 3, 2, 2, 2, 294, 295, 7, 41, 2, 2, 295, 60, 3, 2, 2, 2, 296, 297, 9, 10, 2, 2, 297, 298, 3, 2, 2, 2, 298, 299, 8, 31, 2, 2, 299, 62, 3, 2, 2, 2, 300, 301, 9, 11, 2, 2, 301, 64, 3, 2, 2, 2, 302, 303, 9, 12, 2, 2, 303, 66, 3, 2, 2, 2, 304, 305, 9, 13, 2, 2, 305, 68, 3, 2, 2, 2, 306, 307, 9, 14, 2, 2, 307, 70, 3, 2, 2, 2, 308, 309, 9, 15, 2, 2, 309, 72, 3, 2, 2, 2, 310, 311, 9, 16, 2, 2, 311, 74, 3, 2, 2, 2, 312, 313, 9, 17, 2, 2, 313, 76, 3, 2, 2, 2, 314, 315, 9, 18, 2, 2, 315, 78, 3, 2, 2, 2, 316, 317, 9, 19, 2, 2, 317, 80, 3, 2, 2, 2, 318, 319, 9, 20, 2, 2, 319, 82, 3, 2, 2, 2, 320, 321, 9, 21, 2, 2, 321, 84, 3, 2, 2, 2, 322, 323, 9, 22, 2, 2, 323, 86, 3, 2, 2, 2, 324, 325, 9, 23, 2, 2, 325, 88, 3, 2, 2, 2, 326, 327, 9, 24, 2, 2, 327, 90, 3, 2, 2, 2, 328, 329, 9, 25, 2, 2, 329, 92, 3, 2, 2, 2, 330, 331, 9, 26, 2, 2, 331, 94, 3, 2, 2, 2, 332, 333, 9, 27, 2, 2, 333, 96, 3, 2, 2, 2, 334, 335, 9, 28, 2, 2, 335, 98, 3, 2, 2, 2, 336, 337, 9, 29, 2, 2, 337, 100, 3, 2, 2, 2, 338, 339, 9, 30, 2, 2, 339, 102, 3, 2, 2, 2, 340, 341, 9, 31, 2, 2, 341, 104, 3, 2, 2, 2, 342, 343, 9, 32, 2, 2, 343, 106, 3, 2, 2, 2, 344, 345, 9, 33, 2, 2, 345, 108, 3, 2, 2, 2, 346, 347, 9, 34, 2, 2, 347, 110, 3, 2, 2, 2, 348, 349, 9, 35, 2, 2, 349, 112, 3, 2, 2, 2, 350, 351, 9, 36, 2, 2, 351, 114, 3, 2, 2, 2, 352, 353, 9, 37, 2, 2, 353, 116, 3, 2, 2, 2, 24, 2, 202, 204, 212, 214, 222, 230, 236, 239, 244, 250, 253, 257, 262, 264, 270, 274, 279, 281, 283, 289, 291, 3, 2, 3, 2] \ No newline at end of file diff --git a/v5/pkg/parser/TSLLexer.tokens b/v5/pkg/parser/TSLLexer.tokens new file mode 100644 index 0000000..02c61e1 --- /dev/null +++ b/v5/pkg/parser/TSLLexer.tokens @@ -0,0 +1,47 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +T__13=14 +T__14=15 +T__15=16 +T__16=17 +K_LIKE=18 +K_ILIKE=19 +K_AND=20 +K_OR=21 +K_BETWEEN=22 +K_IN=23 +K_IS=24 +K_NULL=25 +K_NOT=26 +IDENTIFIER=27 +NUMERIC_LITERAL=28 +STRING_LITERAL=29 +SPACES=30 +'('=1 +','=2 +')'=3 +'<'=4 +'<='=5 +'>'=6 +'>='=7 +'='=8 +'!='=9 +'<>'=10 +'~='=11 +'~!'=12 +'*'=13 +'/'=14 +'%'=15 +'+'=16 +'-'=17 diff --git a/v5/pkg/parser/tsl_base_listener.go b/v5/pkg/parser/tsl_base_listener.go new file mode 100644 index 0000000..91a4013 --- /dev/null +++ b/v5/pkg/parser/tsl_base_listener.go @@ -0,0 +1,202 @@ +// Code generated from TSL.g4 by ANTLR 4.7.1. DO NOT EDIT. + +package parser // TSL + +import "github.com/antlr/antlr4/runtime/Go/antlr" + +// BaseTSLListener is a complete listener for a parse tree produced by TSLParser. +type BaseTSLListener struct{} + +var _ TSLListener = &BaseTSLListener{} + +// VisitTerminal is called when a terminal node is visited. +func (s *BaseTSLListener) VisitTerminal(node antlr.TerminalNode) {} + +// VisitErrorNode is called when an error node is visited. +func (s *BaseTSLListener) VisitErrorNode(node antlr.ErrorNode) {} + +// EnterEveryRule is called when any rule is entered. +func (s *BaseTSLListener) EnterEveryRule(ctx antlr.ParserRuleContext) {} + +// ExitEveryRule is called when any rule is exited. +func (s *BaseTSLListener) ExitEveryRule(ctx antlr.ParserRuleContext) {} + +// EnterStart is called when production start is entered. +func (s *BaseTSLListener) EnterStart(ctx *StartContext) {} + +// ExitStart is called when production start is exited. +func (s *BaseTSLListener) ExitStart(ctx *StartContext) {} + +// EnterPar is called when production Par is entered. +func (s *BaseTSLListener) EnterPar(ctx *ParContext) {} + +// ExitPar is called when production Par is exited. +func (s *BaseTSLListener) ExitPar(ctx *ParContext) {} + +// EnterNot is called when production Not is entered. +func (s *BaseTSLListener) EnterNot(ctx *NotContext) {} + +// ExitNot is called when production Not is exited. +func (s *BaseTSLListener) ExitNot(ctx *NotContext) {} + +// EnterLike is called when production Like is entered. +func (s *BaseTSLListener) EnterLike(ctx *LikeContext) {} + +// ExitLike is called when production Like is exited. +func (s *BaseTSLListener) ExitLike(ctx *LikeContext) {} + +// EnterOr is called when production Or is entered. +func (s *BaseTSLListener) EnterOr(ctx *OrContext) {} + +// ExitOr is called when production Or is exited. +func (s *BaseTSLListener) ExitOr(ctx *OrContext) {} + +// EnterIn is called when production In is entered. +func (s *BaseTSLListener) EnterIn(ctx *InContext) {} + +// ExitIn is called when production In is exited. +func (s *BaseTSLListener) ExitIn(ctx *InContext) {} + +// EnterIsLiteral is called when production IsLiteral is entered. +func (s *BaseTSLListener) EnterIsLiteral(ctx *IsLiteralContext) {} + +// ExitIsLiteral is called when production IsLiteral is exited. +func (s *BaseTSLListener) ExitIsLiteral(ctx *IsLiteralContext) {} + +// EnterAnd is called when production And is entered. +func (s *BaseTSLListener) EnterAnd(ctx *AndContext) {} + +// ExitAnd is called when production And is exited. +func (s *BaseTSLListener) ExitAnd(ctx *AndContext) {} + +// EnterBetween is called when production Between is entered. +func (s *BaseTSLListener) EnterBetween(ctx *BetweenContext) {} + +// ExitBetween is called when production Between is exited. +func (s *BaseTSLListener) ExitBetween(ctx *BetweenContext) {} + +// EnterStringOps is called when production StringOps is entered. +func (s *BaseTSLListener) EnterStringOps(ctx *StringOpsContext) {} + +// ExitStringOps is called when production StringOps is exited. +func (s *BaseTSLListener) ExitStringOps(ctx *StringOpsContext) {} + +// EnterIsNull is called when production IsNull is entered. +func (s *BaseTSLListener) EnterIsNull(ctx *IsNullContext) {} + +// ExitIsNull is called when production IsNull is exited. +func (s *BaseTSLListener) ExitIsNull(ctx *IsNullContext) {} + +// EnterLiteralOps is called when production LiteralOps is entered. +func (s *BaseTSLListener) EnterLiteralOps(ctx *LiteralOpsContext) {} + +// ExitLiteralOps is called when production LiteralOps is exited. +func (s *BaseTSLListener) ExitLiteralOps(ctx *LiteralOpsContext) {} + +// EnterLiteralOp is called when production literalOp is entered. +func (s *BaseTSLListener) EnterLiteralOp(ctx *LiteralOpContext) {} + +// ExitLiteralOp is called when production literalOp is exited. +func (s *BaseTSLListener) ExitLiteralOp(ctx *LiteralOpContext) {} + +// EnterStringOp is called when production stringOp is entered. +func (s *BaseTSLListener) EnterStringOp(ctx *StringOpContext) {} + +// ExitStringOp is called when production stringOp is exited. +func (s *BaseTSLListener) ExitStringOp(ctx *StringOpContext) {} + +// EnterLikeOp is called when production likeOp is entered. +func (s *BaseTSLListener) EnterLikeOp(ctx *LikeOpContext) {} + +// ExitLikeOp is called when production likeOp is exited. +func (s *BaseTSLListener) ExitLikeOp(ctx *LikeOpContext) {} + +// EnterDatabaseName is called when production databaseName is entered. +func (s *BaseTSLListener) EnterDatabaseName(ctx *DatabaseNameContext) {} + +// ExitDatabaseName is called when production databaseName is exited. +func (s *BaseTSLListener) ExitDatabaseName(ctx *DatabaseNameContext) {} + +// EnterTableName is called when production tableName is entered. +func (s *BaseTSLListener) EnterTableName(ctx *TableNameContext) {} + +// ExitTableName is called when production tableName is exited. +func (s *BaseTSLListener) ExitTableName(ctx *TableNameContext) {} + +// EnterColumnName is called when production columnName is entered. +func (s *BaseTSLListener) EnterColumnName(ctx *ColumnNameContext) {} + +// ExitColumnName is called when production columnName is exited. +func (s *BaseTSLListener) ExitColumnName(ctx *ColumnNameContext) {} + +// EnterNumberLiteral is called when production NumberLiteral is entered. +func (s *BaseTSLListener) EnterNumberLiteral(ctx *NumberLiteralContext) {} + +// ExitNumberLiteral is called when production NumberLiteral is exited. +func (s *BaseTSLListener) ExitNumberLiteral(ctx *NumberLiteralContext) {} + +// EnterStringLiteral is called when production StringLiteral is entered. +func (s *BaseTSLListener) EnterStringLiteral(ctx *StringLiteralContext) {} + +// ExitStringLiteral is called when production StringLiteral is exited. +func (s *BaseTSLListener) ExitStringLiteral(ctx *StringLiteralContext) {} + +// EnterMathPar is called when production MathPar is entered. +func (s *BaseTSLListener) EnterMathPar(ctx *MathParContext) {} + +// ExitMathPar is called when production MathPar is exited. +func (s *BaseTSLListener) ExitMathPar(ctx *MathParContext) {} + +// EnterModOps is called when production ModOps is entered. +func (s *BaseTSLListener) EnterModOps(ctx *ModOpsContext) {} + +// ExitModOps is called when production ModOps is exited. +func (s *BaseTSLListener) ExitModOps(ctx *ModOpsContext) {} + +// EnterSubOps is called when production SubOps is entered. +func (s *BaseTSLListener) EnterSubOps(ctx *SubOpsContext) {} + +// ExitSubOps is called when production SubOps is exited. +func (s *BaseTSLListener) ExitSubOps(ctx *SubOpsContext) {} + +// EnterMulOps is called when production MulOps is entered. +func (s *BaseTSLListener) EnterMulOps(ctx *MulOpsContext) {} + +// ExitMulOps is called when production MulOps is exited. +func (s *BaseTSLListener) ExitMulOps(ctx *MulOpsContext) {} + +// EnterDivOps is called when production DivOps is entered. +func (s *BaseTSLListener) EnterDivOps(ctx *DivOpsContext) {} + +// ExitDivOps is called when production DivOps is exited. +func (s *BaseTSLListener) ExitDivOps(ctx *DivOpsContext) {} + +// EnterColumnIdentifier is called when production ColumnIdentifier is entered. +func (s *BaseTSLListener) EnterColumnIdentifier(ctx *ColumnIdentifierContext) {} + +// ExitColumnIdentifier is called when production ColumnIdentifier is exited. +func (s *BaseTSLListener) ExitColumnIdentifier(ctx *ColumnIdentifierContext) {} + +// EnterAddOps is called when production AddOps is entered. +func (s *BaseTSLListener) EnterAddOps(ctx *AddOpsContext) {} + +// ExitAddOps is called when production AddOps is exited. +func (s *BaseTSLListener) ExitAddOps(ctx *AddOpsContext) {} + +// EnterSignedNumber is called when production signedNumber is entered. +func (s *BaseTSLListener) EnterSignedNumber(ctx *SignedNumberContext) {} + +// ExitSignedNumber is called when production signedNumber is exited. +func (s *BaseTSLListener) ExitSignedNumber(ctx *SignedNumberContext) {} + +// EnterStringValue is called when production stringValue is entered. +func (s *BaseTSLListener) EnterStringValue(ctx *StringValueContext) {} + +// ExitStringValue is called when production stringValue is exited. +func (s *BaseTSLListener) ExitStringValue(ctx *StringValueContext) {} + +// EnterKeyNot is called when production keyNot is entered. +func (s *BaseTSLListener) EnterKeyNot(ctx *KeyNotContext) {} + +// ExitKeyNot is called when production keyNot is exited. +func (s *BaseTSLListener) ExitKeyNot(ctx *KeyNotContext) {} diff --git a/v5/pkg/parser/tsl_lexer.go b/v5/pkg/parser/tsl_lexer.go new file mode 100644 index 0000000..583a2ca --- /dev/null +++ b/v5/pkg/parser/tsl_lexer.go @@ -0,0 +1,277 @@ +// Code generated from TSL.g4 by ANTLR 4.7.1. DO NOT EDIT. + +package parser + +import ( + "fmt" + "unicode" + + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +// Suppress unused import error +var _ = fmt.Printf +var _ = unicode.IsLetter + +var serializedLexerAtn = []uint16{ + 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 32, 354, + 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, + 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, + 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, + 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, + 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, + 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, + 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, + 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, + 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, + 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, + 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 3, 2, 3, 2, 3, + 3, 3, 3, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, + 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 12, 3, + 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, + 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 20, 3, + 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 21, 3, 22, 3, 22, + 3, 22, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 24, 3, + 24, 3, 24, 3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 27, + 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 28, 7, 28, 203, 10, 28, 12, + 28, 14, 28, 206, 11, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 7, 28, 213, + 10, 28, 12, 28, 14, 28, 216, 11, 28, 3, 28, 3, 28, 3, 28, 7, 28, 221, 10, + 28, 12, 28, 14, 28, 224, 11, 28, 3, 28, 3, 28, 3, 28, 7, 28, 229, 10, 28, + 12, 28, 14, 28, 232, 11, 28, 3, 28, 7, 28, 235, 10, 28, 12, 28, 14, 28, + 238, 11, 28, 5, 28, 240, 10, 28, 3, 29, 6, 29, 243, 10, 29, 13, 29, 14, + 29, 244, 3, 29, 3, 29, 7, 29, 249, 10, 29, 12, 29, 14, 29, 252, 11, 29, + 5, 29, 254, 10, 29, 3, 29, 3, 29, 5, 29, 258, 10, 29, 3, 29, 6, 29, 261, + 10, 29, 13, 29, 14, 29, 262, 5, 29, 265, 10, 29, 3, 29, 3, 29, 6, 29, 269, + 10, 29, 13, 29, 14, 29, 270, 3, 29, 3, 29, 5, 29, 275, 10, 29, 3, 29, 6, + 29, 278, 10, 29, 13, 29, 14, 29, 279, 5, 29, 282, 10, 29, 5, 29, 284, 10, + 29, 3, 30, 3, 30, 3, 30, 3, 30, 7, 30, 290, 10, 30, 12, 30, 14, 30, 293, + 11, 30, 3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 33, + 3, 33, 3, 34, 3, 34, 3, 35, 3, 35, 3, 36, 3, 36, 3, 37, 3, 37, 3, 38, 3, + 38, 3, 39, 3, 39, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3, 43, + 3, 44, 3, 44, 3, 45, 3, 45, 3, 46, 3, 46, 3, 47, 3, 47, 3, 48, 3, 48, 3, + 49, 3, 49, 3, 50, 3, 50, 3, 51, 3, 51, 3, 52, 3, 52, 3, 53, 3, 53, 3, 54, + 3, 54, 3, 55, 3, 55, 3, 56, 3, 56, 3, 57, 3, 57, 3, 58, 3, 58, 2, 2, 59, + 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, + 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, + 22, 43, 23, 45, 24, 47, 25, 49, 26, 51, 27, 53, 28, 55, 29, 57, 30, 59, + 31, 61, 32, 63, 2, 65, 2, 67, 2, 69, 2, 71, 2, 73, 2, 75, 2, 77, 2, 79, + 2, 81, 2, 83, 2, 85, 2, 87, 2, 89, 2, 91, 2, 93, 2, 95, 2, 97, 2, 99, 2, + 101, 2, 103, 2, 105, 2, 107, 2, 109, 2, 111, 2, 113, 2, 115, 2, 3, 2, 38, + 3, 2, 36, 36, 3, 2, 98, 98, 3, 2, 95, 95, 5, 2, 67, 92, 97, 97, 99, 124, + 6, 2, 48, 59, 67, 92, 97, 97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, + 124, 4, 2, 45, 45, 47, 47, 3, 2, 41, 41, 5, 2, 11, 13, 15, 15, 34, 34, + 3, 2, 50, 59, 4, 2, 67, 67, 99, 99, 4, 2, 68, 68, 100, 100, 4, 2, 69, 69, + 101, 101, 4, 2, 70, 70, 102, 102, 4, 2, 71, 71, 103, 103, 4, 2, 72, 72, + 104, 104, 4, 2, 73, 73, 105, 105, 4, 2, 74, 74, 106, 106, 4, 2, 75, 75, + 107, 107, 4, 2, 76, 76, 108, 108, 4, 2, 77, 77, 109, 109, 4, 2, 78, 78, + 110, 110, 4, 2, 79, 79, 111, 111, 4, 2, 80, 80, 112, 112, 4, 2, 81, 81, + 113, 113, 4, 2, 82, 82, 114, 114, 4, 2, 83, 83, 115, 115, 4, 2, 84, 84, + 116, 116, 4, 2, 85, 85, 117, 117, 4, 2, 86, 86, 118, 118, 4, 2, 87, 87, + 119, 119, 4, 2, 88, 88, 120, 120, 4, 2, 89, 89, 121, 121, 4, 2, 90, 90, + 122, 122, 4, 2, 91, 91, 123, 123, 4, 2, 92, 92, 124, 124, 2, 349, 2, 3, + 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, + 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, + 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, + 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, + 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, + 2, 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 2, 49, 3, + 2, 2, 2, 2, 51, 3, 2, 2, 2, 2, 53, 3, 2, 2, 2, 2, 55, 3, 2, 2, 2, 2, 57, + 3, 2, 2, 2, 2, 59, 3, 2, 2, 2, 2, 61, 3, 2, 2, 2, 3, 117, 3, 2, 2, 2, 5, + 119, 3, 2, 2, 2, 7, 121, 3, 2, 2, 2, 9, 123, 3, 2, 2, 2, 11, 125, 3, 2, + 2, 2, 13, 128, 3, 2, 2, 2, 15, 130, 3, 2, 2, 2, 17, 133, 3, 2, 2, 2, 19, + 135, 3, 2, 2, 2, 21, 138, 3, 2, 2, 2, 23, 141, 3, 2, 2, 2, 25, 144, 3, + 2, 2, 2, 27, 147, 3, 2, 2, 2, 29, 149, 3, 2, 2, 2, 31, 151, 3, 2, 2, 2, + 33, 153, 3, 2, 2, 2, 35, 155, 3, 2, 2, 2, 37, 157, 3, 2, 2, 2, 39, 162, + 3, 2, 2, 2, 41, 168, 3, 2, 2, 2, 43, 172, 3, 2, 2, 2, 45, 175, 3, 2, 2, + 2, 47, 183, 3, 2, 2, 2, 49, 186, 3, 2, 2, 2, 51, 189, 3, 2, 2, 2, 53, 194, + 3, 2, 2, 2, 55, 239, 3, 2, 2, 2, 57, 283, 3, 2, 2, 2, 59, 285, 3, 2, 2, + 2, 61, 296, 3, 2, 2, 2, 63, 300, 3, 2, 2, 2, 65, 302, 3, 2, 2, 2, 67, 304, + 3, 2, 2, 2, 69, 306, 3, 2, 2, 2, 71, 308, 3, 2, 2, 2, 73, 310, 3, 2, 2, + 2, 75, 312, 3, 2, 2, 2, 77, 314, 3, 2, 2, 2, 79, 316, 3, 2, 2, 2, 81, 318, + 3, 2, 2, 2, 83, 320, 3, 2, 2, 2, 85, 322, 3, 2, 2, 2, 87, 324, 3, 2, 2, + 2, 89, 326, 3, 2, 2, 2, 91, 328, 3, 2, 2, 2, 93, 330, 3, 2, 2, 2, 95, 332, + 3, 2, 2, 2, 97, 334, 3, 2, 2, 2, 99, 336, 3, 2, 2, 2, 101, 338, 3, 2, 2, + 2, 103, 340, 3, 2, 2, 2, 105, 342, 3, 2, 2, 2, 107, 344, 3, 2, 2, 2, 109, + 346, 3, 2, 2, 2, 111, 348, 3, 2, 2, 2, 113, 350, 3, 2, 2, 2, 115, 352, + 3, 2, 2, 2, 117, 118, 7, 42, 2, 2, 118, 4, 3, 2, 2, 2, 119, 120, 7, 46, + 2, 2, 120, 6, 3, 2, 2, 2, 121, 122, 7, 43, 2, 2, 122, 8, 3, 2, 2, 2, 123, + 124, 7, 62, 2, 2, 124, 10, 3, 2, 2, 2, 125, 126, 7, 62, 2, 2, 126, 127, + 7, 63, 2, 2, 127, 12, 3, 2, 2, 2, 128, 129, 7, 64, 2, 2, 129, 14, 3, 2, + 2, 2, 130, 131, 7, 64, 2, 2, 131, 132, 7, 63, 2, 2, 132, 16, 3, 2, 2, 2, + 133, 134, 7, 63, 2, 2, 134, 18, 3, 2, 2, 2, 135, 136, 7, 35, 2, 2, 136, + 137, 7, 63, 2, 2, 137, 20, 3, 2, 2, 2, 138, 139, 7, 62, 2, 2, 139, 140, + 7, 64, 2, 2, 140, 22, 3, 2, 2, 2, 141, 142, 7, 128, 2, 2, 142, 143, 7, + 63, 2, 2, 143, 24, 3, 2, 2, 2, 144, 145, 7, 128, 2, 2, 145, 146, 7, 35, + 2, 2, 146, 26, 3, 2, 2, 2, 147, 148, 7, 44, 2, 2, 148, 28, 3, 2, 2, 2, + 149, 150, 7, 49, 2, 2, 150, 30, 3, 2, 2, 2, 151, 152, 7, 39, 2, 2, 152, + 32, 3, 2, 2, 2, 153, 154, 7, 45, 2, 2, 154, 34, 3, 2, 2, 2, 155, 156, 7, + 47, 2, 2, 156, 36, 3, 2, 2, 2, 157, 158, 5, 87, 44, 2, 158, 159, 5, 81, + 41, 2, 159, 160, 5, 85, 43, 2, 160, 161, 5, 73, 37, 2, 161, 38, 3, 2, 2, + 2, 162, 163, 5, 81, 41, 2, 163, 164, 5, 87, 44, 2, 164, 165, 5, 81, 41, + 2, 165, 166, 5, 85, 43, 2, 166, 167, 5, 73, 37, 2, 167, 40, 3, 2, 2, 2, + 168, 169, 5, 65, 33, 2, 169, 170, 5, 91, 46, 2, 170, 171, 5, 71, 36, 2, + 171, 42, 3, 2, 2, 2, 172, 173, 5, 93, 47, 2, 173, 174, 5, 99, 50, 2, 174, + 44, 3, 2, 2, 2, 175, 176, 5, 67, 34, 2, 176, 177, 5, 73, 37, 2, 177, 178, + 5, 103, 52, 2, 178, 179, 5, 109, 55, 2, 179, 180, 5, 73, 37, 2, 180, 181, + 5, 73, 37, 2, 181, 182, 5, 91, 46, 2, 182, 46, 3, 2, 2, 2, 183, 184, 5, + 81, 41, 2, 184, 185, 5, 91, 46, 2, 185, 48, 3, 2, 2, 2, 186, 187, 5, 81, + 41, 2, 187, 188, 5, 101, 51, 2, 188, 50, 3, 2, 2, 2, 189, 190, 5, 91, 46, + 2, 190, 191, 5, 105, 53, 2, 191, 192, 5, 87, 44, 2, 192, 193, 5, 87, 44, + 2, 193, 52, 3, 2, 2, 2, 194, 195, 5, 91, 46, 2, 195, 196, 5, 93, 47, 2, + 196, 197, 5, 103, 52, 2, 197, 54, 3, 2, 2, 2, 198, 204, 7, 36, 2, 2, 199, + 203, 10, 2, 2, 2, 200, 201, 7, 36, 2, 2, 201, 203, 7, 36, 2, 2, 202, 199, + 3, 2, 2, 2, 202, 200, 3, 2, 2, 2, 203, 206, 3, 2, 2, 2, 204, 202, 3, 2, + 2, 2, 204, 205, 3, 2, 2, 2, 205, 207, 3, 2, 2, 2, 206, 204, 3, 2, 2, 2, + 207, 240, 7, 36, 2, 2, 208, 214, 7, 98, 2, 2, 209, 213, 10, 3, 2, 2, 210, + 211, 7, 98, 2, 2, 211, 213, 7, 98, 2, 2, 212, 209, 3, 2, 2, 2, 212, 210, + 3, 2, 2, 2, 213, 216, 3, 2, 2, 2, 214, 212, 3, 2, 2, 2, 214, 215, 3, 2, + 2, 2, 215, 217, 3, 2, 2, 2, 216, 214, 3, 2, 2, 2, 217, 240, 7, 98, 2, 2, + 218, 222, 7, 93, 2, 2, 219, 221, 10, 4, 2, 2, 220, 219, 3, 2, 2, 2, 221, + 224, 3, 2, 2, 2, 222, 220, 3, 2, 2, 2, 222, 223, 3, 2, 2, 2, 223, 225, + 3, 2, 2, 2, 224, 222, 3, 2, 2, 2, 225, 240, 7, 95, 2, 2, 226, 230, 9, 5, + 2, 2, 227, 229, 9, 6, 2, 2, 228, 227, 3, 2, 2, 2, 229, 232, 3, 2, 2, 2, + 230, 228, 3, 2, 2, 2, 230, 231, 3, 2, 2, 2, 231, 236, 3, 2, 2, 2, 232, + 230, 3, 2, 2, 2, 233, 235, 9, 7, 2, 2, 234, 233, 3, 2, 2, 2, 235, 238, + 3, 2, 2, 2, 236, 234, 3, 2, 2, 2, 236, 237, 3, 2, 2, 2, 237, 240, 3, 2, + 2, 2, 238, 236, 3, 2, 2, 2, 239, 198, 3, 2, 2, 2, 239, 208, 3, 2, 2, 2, + 239, 218, 3, 2, 2, 2, 239, 226, 3, 2, 2, 2, 240, 56, 3, 2, 2, 2, 241, 243, + 5, 63, 32, 2, 242, 241, 3, 2, 2, 2, 243, 244, 3, 2, 2, 2, 244, 242, 3, + 2, 2, 2, 244, 245, 3, 2, 2, 2, 245, 253, 3, 2, 2, 2, 246, 250, 7, 48, 2, + 2, 247, 249, 5, 63, 32, 2, 248, 247, 3, 2, 2, 2, 249, 252, 3, 2, 2, 2, + 250, 248, 3, 2, 2, 2, 250, 251, 3, 2, 2, 2, 251, 254, 3, 2, 2, 2, 252, + 250, 3, 2, 2, 2, 253, 246, 3, 2, 2, 2, 253, 254, 3, 2, 2, 2, 254, 264, + 3, 2, 2, 2, 255, 257, 5, 73, 37, 2, 256, 258, 9, 8, 2, 2, 257, 256, 3, + 2, 2, 2, 257, 258, 3, 2, 2, 2, 258, 260, 3, 2, 2, 2, 259, 261, 5, 63, 32, + 2, 260, 259, 3, 2, 2, 2, 261, 262, 3, 2, 2, 2, 262, 260, 3, 2, 2, 2, 262, + 263, 3, 2, 2, 2, 263, 265, 3, 2, 2, 2, 264, 255, 3, 2, 2, 2, 264, 265, + 3, 2, 2, 2, 265, 284, 3, 2, 2, 2, 266, 268, 7, 48, 2, 2, 267, 269, 5, 63, + 32, 2, 268, 267, 3, 2, 2, 2, 269, 270, 3, 2, 2, 2, 270, 268, 3, 2, 2, 2, + 270, 271, 3, 2, 2, 2, 271, 281, 3, 2, 2, 2, 272, 274, 5, 73, 37, 2, 273, + 275, 9, 8, 2, 2, 274, 273, 3, 2, 2, 2, 274, 275, 3, 2, 2, 2, 275, 277, + 3, 2, 2, 2, 276, 278, 5, 63, 32, 2, 277, 276, 3, 2, 2, 2, 278, 279, 3, + 2, 2, 2, 279, 277, 3, 2, 2, 2, 279, 280, 3, 2, 2, 2, 280, 282, 3, 2, 2, + 2, 281, 272, 3, 2, 2, 2, 281, 282, 3, 2, 2, 2, 282, 284, 3, 2, 2, 2, 283, + 242, 3, 2, 2, 2, 283, 266, 3, 2, 2, 2, 284, 58, 3, 2, 2, 2, 285, 291, 7, + 41, 2, 2, 286, 290, 10, 9, 2, 2, 287, 288, 7, 41, 2, 2, 288, 290, 7, 41, + 2, 2, 289, 286, 3, 2, 2, 2, 289, 287, 3, 2, 2, 2, 290, 293, 3, 2, 2, 2, + 291, 289, 3, 2, 2, 2, 291, 292, 3, 2, 2, 2, 292, 294, 3, 2, 2, 2, 293, + 291, 3, 2, 2, 2, 294, 295, 7, 41, 2, 2, 295, 60, 3, 2, 2, 2, 296, 297, + 9, 10, 2, 2, 297, 298, 3, 2, 2, 2, 298, 299, 8, 31, 2, 2, 299, 62, 3, 2, + 2, 2, 300, 301, 9, 11, 2, 2, 301, 64, 3, 2, 2, 2, 302, 303, 9, 12, 2, 2, + 303, 66, 3, 2, 2, 2, 304, 305, 9, 13, 2, 2, 305, 68, 3, 2, 2, 2, 306, 307, + 9, 14, 2, 2, 307, 70, 3, 2, 2, 2, 308, 309, 9, 15, 2, 2, 309, 72, 3, 2, + 2, 2, 310, 311, 9, 16, 2, 2, 311, 74, 3, 2, 2, 2, 312, 313, 9, 17, 2, 2, + 313, 76, 3, 2, 2, 2, 314, 315, 9, 18, 2, 2, 315, 78, 3, 2, 2, 2, 316, 317, + 9, 19, 2, 2, 317, 80, 3, 2, 2, 2, 318, 319, 9, 20, 2, 2, 319, 82, 3, 2, + 2, 2, 320, 321, 9, 21, 2, 2, 321, 84, 3, 2, 2, 2, 322, 323, 9, 22, 2, 2, + 323, 86, 3, 2, 2, 2, 324, 325, 9, 23, 2, 2, 325, 88, 3, 2, 2, 2, 326, 327, + 9, 24, 2, 2, 327, 90, 3, 2, 2, 2, 328, 329, 9, 25, 2, 2, 329, 92, 3, 2, + 2, 2, 330, 331, 9, 26, 2, 2, 331, 94, 3, 2, 2, 2, 332, 333, 9, 27, 2, 2, + 333, 96, 3, 2, 2, 2, 334, 335, 9, 28, 2, 2, 335, 98, 3, 2, 2, 2, 336, 337, + 9, 29, 2, 2, 337, 100, 3, 2, 2, 2, 338, 339, 9, 30, 2, 2, 339, 102, 3, + 2, 2, 2, 340, 341, 9, 31, 2, 2, 341, 104, 3, 2, 2, 2, 342, 343, 9, 32, + 2, 2, 343, 106, 3, 2, 2, 2, 344, 345, 9, 33, 2, 2, 345, 108, 3, 2, 2, 2, + 346, 347, 9, 34, 2, 2, 347, 110, 3, 2, 2, 2, 348, 349, 9, 35, 2, 2, 349, + 112, 3, 2, 2, 2, 350, 351, 9, 36, 2, 2, 351, 114, 3, 2, 2, 2, 352, 353, + 9, 37, 2, 2, 353, 116, 3, 2, 2, 2, 24, 2, 202, 204, 212, 214, 222, 230, + 236, 239, 244, 250, 253, 257, 262, 264, 270, 274, 279, 281, 283, 289, 291, + 3, 2, 3, 2, +} + +var lexerDeserializer = antlr.NewATNDeserializer(nil) +var lexerAtn = lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn) + +var lexerChannelNames = []string{ + "DEFAULT_TOKEN_CHANNEL", "HIDDEN", +} + +var lexerModeNames = []string{ + "DEFAULT_MODE", +} + +var lexerLiteralNames = []string{ + "", "'('", "','", "')'", "'<'", "'<='", "'>'", "'>='", "'='", "'!='", "'<>'", + "'~='", "'~!'", "'*'", "'/'", "'%'", "'+'", "'-'", +} + +var lexerSymbolicNames = []string{ + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + "K_LIKE", "K_ILIKE", "K_AND", "K_OR", "K_BETWEEN", "K_IN", "K_IS", "K_NULL", + "K_NOT", "IDENTIFIER", "NUMERIC_LITERAL", "STRING_LITERAL", "SPACES", +} + +var lexerRuleNames = []string{ + "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8", + "T__9", "T__10", "T__11", "T__12", "T__13", "T__14", "T__15", "T__16", + "K_LIKE", "K_ILIKE", "K_AND", "K_OR", "K_BETWEEN", "K_IN", "K_IS", "K_NULL", + "K_NOT", "IDENTIFIER", "NUMERIC_LITERAL", "STRING_LITERAL", "SPACES", "DIGIT", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", +} + +type TSLLexer struct { + *antlr.BaseLexer + channelNames []string + modeNames []string + // TODO: EOF string +} + +var lexerDecisionToDFA = make([]*antlr.DFA, len(lexerAtn.DecisionToState)) + +func init() { + for index, ds := range lexerAtn.DecisionToState { + lexerDecisionToDFA[index] = antlr.NewDFA(ds, index) + } +} + +func NewTSLLexer(input antlr.CharStream) *TSLLexer { + + l := new(TSLLexer) + + l.BaseLexer = antlr.NewBaseLexer(input) + l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache()) + + l.channelNames = lexerChannelNames + l.modeNames = lexerModeNames + l.RuleNames = lexerRuleNames + l.LiteralNames = lexerLiteralNames + l.SymbolicNames = lexerSymbolicNames + l.GrammarFileName = "TSL.g4" + // TODO: l.EOF = antlr.TokenEOF + + return l +} + +// TSLLexer tokens. +const ( + TSLLexerT__0 = 1 + TSLLexerT__1 = 2 + TSLLexerT__2 = 3 + TSLLexerT__3 = 4 + TSLLexerT__4 = 5 + TSLLexerT__5 = 6 + TSLLexerT__6 = 7 + TSLLexerT__7 = 8 + TSLLexerT__8 = 9 + TSLLexerT__9 = 10 + TSLLexerT__10 = 11 + TSLLexerT__11 = 12 + TSLLexerT__12 = 13 + TSLLexerT__13 = 14 + TSLLexerT__14 = 15 + TSLLexerT__15 = 16 + TSLLexerT__16 = 17 + TSLLexerK_LIKE = 18 + TSLLexerK_ILIKE = 19 + TSLLexerK_AND = 20 + TSLLexerK_OR = 21 + TSLLexerK_BETWEEN = 22 + TSLLexerK_IN = 23 + TSLLexerK_IS = 24 + TSLLexerK_NULL = 25 + TSLLexerK_NOT = 26 + TSLLexerIDENTIFIER = 27 + TSLLexerNUMERIC_LITERAL = 28 + TSLLexerSTRING_LITERAL = 29 + TSLLexerSPACES = 30 +) diff --git a/v5/pkg/parser/tsl_listener.go b/v5/pkg/parser/tsl_listener.go new file mode 100644 index 0000000..c6e69a0 --- /dev/null +++ b/v5/pkg/parser/tsl_listener.go @@ -0,0 +1,190 @@ +// Code generated from TSL.g4 by ANTLR 4.7.1. DO NOT EDIT. + +package parser // TSL + +import "github.com/antlr/antlr4/runtime/Go/antlr" + +// TSLListener is a complete listener for a parse tree produced by TSLParser. +type TSLListener interface { + antlr.ParseTreeListener + + // EnterStart is called when entering the start production. + EnterStart(c *StartContext) + + // EnterPar is called when entering the Par production. + EnterPar(c *ParContext) + + // EnterNot is called when entering the Not production. + EnterNot(c *NotContext) + + // EnterLike is called when entering the Like production. + EnterLike(c *LikeContext) + + // EnterOr is called when entering the Or production. + EnterOr(c *OrContext) + + // EnterIn is called when entering the In production. + EnterIn(c *InContext) + + // EnterIsLiteral is called when entering the IsLiteral production. + EnterIsLiteral(c *IsLiteralContext) + + // EnterAnd is called when entering the And production. + EnterAnd(c *AndContext) + + // EnterBetween is called when entering the Between production. + EnterBetween(c *BetweenContext) + + // EnterStringOps is called when entering the StringOps production. + EnterStringOps(c *StringOpsContext) + + // EnterIsNull is called when entering the IsNull production. + EnterIsNull(c *IsNullContext) + + // EnterLiteralOps is called when entering the LiteralOps production. + EnterLiteralOps(c *LiteralOpsContext) + + // EnterLiteralOp is called when entering the literalOp production. + EnterLiteralOp(c *LiteralOpContext) + + // EnterStringOp is called when entering the stringOp production. + EnterStringOp(c *StringOpContext) + + // EnterLikeOp is called when entering the likeOp production. + EnterLikeOp(c *LikeOpContext) + + // EnterDatabaseName is called when entering the databaseName production. + EnterDatabaseName(c *DatabaseNameContext) + + // EnterTableName is called when entering the tableName production. + EnterTableName(c *TableNameContext) + + // EnterColumnName is called when entering the columnName production. + EnterColumnName(c *ColumnNameContext) + + // EnterNumberLiteral is called when entering the NumberLiteral production. + EnterNumberLiteral(c *NumberLiteralContext) + + // EnterStringLiteral is called when entering the StringLiteral production. + EnterStringLiteral(c *StringLiteralContext) + + // EnterMathPar is called when entering the MathPar production. + EnterMathPar(c *MathParContext) + + // EnterModOps is called when entering the ModOps production. + EnterModOps(c *ModOpsContext) + + // EnterSubOps is called when entering the SubOps production. + EnterSubOps(c *SubOpsContext) + + // EnterMulOps is called when entering the MulOps production. + EnterMulOps(c *MulOpsContext) + + // EnterDivOps is called when entering the DivOps production. + EnterDivOps(c *DivOpsContext) + + // EnterColumnIdentifier is called when entering the ColumnIdentifier production. + EnterColumnIdentifier(c *ColumnIdentifierContext) + + // EnterAddOps is called when entering the AddOps production. + EnterAddOps(c *AddOpsContext) + + // EnterSignedNumber is called when entering the signedNumber production. + EnterSignedNumber(c *SignedNumberContext) + + // EnterStringValue is called when entering the stringValue production. + EnterStringValue(c *StringValueContext) + + // EnterKeyNot is called when entering the keyNot production. + EnterKeyNot(c *KeyNotContext) + + // ExitStart is called when exiting the start production. + ExitStart(c *StartContext) + + // ExitPar is called when exiting the Par production. + ExitPar(c *ParContext) + + // ExitNot is called when exiting the Not production. + ExitNot(c *NotContext) + + // ExitLike is called when exiting the Like production. + ExitLike(c *LikeContext) + + // ExitOr is called when exiting the Or production. + ExitOr(c *OrContext) + + // ExitIn is called when exiting the In production. + ExitIn(c *InContext) + + // ExitIsLiteral is called when exiting the IsLiteral production. + ExitIsLiteral(c *IsLiteralContext) + + // ExitAnd is called when exiting the And production. + ExitAnd(c *AndContext) + + // ExitBetween is called when exiting the Between production. + ExitBetween(c *BetweenContext) + + // ExitStringOps is called when exiting the StringOps production. + ExitStringOps(c *StringOpsContext) + + // ExitIsNull is called when exiting the IsNull production. + ExitIsNull(c *IsNullContext) + + // ExitLiteralOps is called when exiting the LiteralOps production. + ExitLiteralOps(c *LiteralOpsContext) + + // ExitLiteralOp is called when exiting the literalOp production. + ExitLiteralOp(c *LiteralOpContext) + + // ExitStringOp is called when exiting the stringOp production. + ExitStringOp(c *StringOpContext) + + // ExitLikeOp is called when exiting the likeOp production. + ExitLikeOp(c *LikeOpContext) + + // ExitDatabaseName is called when exiting the databaseName production. + ExitDatabaseName(c *DatabaseNameContext) + + // ExitTableName is called when exiting the tableName production. + ExitTableName(c *TableNameContext) + + // ExitColumnName is called when exiting the columnName production. + ExitColumnName(c *ColumnNameContext) + + // ExitNumberLiteral is called when exiting the NumberLiteral production. + ExitNumberLiteral(c *NumberLiteralContext) + + // ExitStringLiteral is called when exiting the StringLiteral production. + ExitStringLiteral(c *StringLiteralContext) + + // ExitMathPar is called when exiting the MathPar production. + ExitMathPar(c *MathParContext) + + // ExitModOps is called when exiting the ModOps production. + ExitModOps(c *ModOpsContext) + + // ExitSubOps is called when exiting the SubOps production. + ExitSubOps(c *SubOpsContext) + + // ExitMulOps is called when exiting the MulOps production. + ExitMulOps(c *MulOpsContext) + + // ExitDivOps is called when exiting the DivOps production. + ExitDivOps(c *DivOpsContext) + + // ExitColumnIdentifier is called when exiting the ColumnIdentifier production. + ExitColumnIdentifier(c *ColumnIdentifierContext) + + // ExitAddOps is called when exiting the AddOps production. + ExitAddOps(c *AddOpsContext) + + // ExitSignedNumber is called when exiting the signedNumber production. + ExitSignedNumber(c *SignedNumberContext) + + // ExitStringValue is called when exiting the stringValue production. + ExitStringValue(c *StringValueContext) + + // ExitKeyNot is called when exiting the keyNot production. + ExitKeyNot(c *KeyNotContext) +} diff --git a/v5/pkg/parser/tsl_parser.go b/v5/pkg/parser/tsl_parser.go new file mode 100644 index 0000000..ff354b9 --- /dev/null +++ b/v5/pkg/parser/tsl_parser.go @@ -0,0 +1,3167 @@ +// Code generated from TSL.g4 by ANTLR 4.7.1. DO NOT EDIT. + +package parser // TSL + +import ( + "fmt" + "reflect" + "strconv" + + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +// Suppress unused import errors +var _ = fmt.Printf +var _ = reflect.Copy +var _ = strconv.Itoa + +var parserATN = []uint16{ + 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 32, 178, + 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, + 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, + 9, 13, 4, 14, 9, 14, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 43, 10, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 5, 3, 51, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 58, + 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 64, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 5, 3, 73, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, + 80, 10, 3, 12, 3, 14, 3, 83, 11, 3, 5, 3, 85, 10, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 95, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 7, 3, 103, 10, 3, 12, 3, 14, 3, 106, 11, 3, 3, 4, 3, 4, 5, + 4, 110, 10, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, + 9, 3, 10, 3, 10, 5, 10, 124, 10, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, + 3, 11, 5, 11, 132, 10, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 138, 10, + 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 144, 10, 11, 3, 11, 3, 11, 3, 11, + 3, 11, 5, 11, 150, 10, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 156, 10, + 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 162, 10, 11, 7, 11, 164, 10, 11, + 12, 11, 14, 11, 167, 11, 11, 3, 12, 5, 12, 170, 10, 12, 3, 12, 3, 12, 3, + 13, 3, 13, 3, 14, 3, 14, 3, 14, 2, 4, 4, 20, 15, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 2, 7, 3, 2, 6, 9, 3, 2, 10, 12, 3, 2, 13, 14, 3, + 2, 20, 21, 3, 2, 18, 19, 2, 195, 2, 28, 3, 2, 2, 2, 4, 94, 3, 2, 2, 2, + 6, 109, 3, 2, 2, 2, 8, 111, 3, 2, 2, 2, 10, 113, 3, 2, 2, 2, 12, 115, 3, + 2, 2, 2, 14, 117, 3, 2, 2, 2, 16, 119, 3, 2, 2, 2, 18, 123, 3, 2, 2, 2, + 20, 131, 3, 2, 2, 2, 22, 169, 3, 2, 2, 2, 24, 173, 3, 2, 2, 2, 26, 175, + 3, 2, 2, 2, 28, 29, 5, 4, 3, 2, 29, 30, 7, 2, 2, 3, 30, 3, 3, 2, 2, 2, + 31, 32, 8, 3, 1, 2, 32, 33, 5, 20, 11, 2, 33, 34, 5, 6, 4, 2, 34, 35, 5, + 18, 10, 2, 35, 95, 3, 2, 2, 2, 36, 37, 5, 20, 11, 2, 37, 38, 5, 8, 5, 2, + 38, 39, 5, 18, 10, 2, 39, 95, 3, 2, 2, 2, 40, 42, 5, 20, 11, 2, 41, 43, + 5, 26, 14, 2, 42, 41, 3, 2, 2, 2, 42, 43, 3, 2, 2, 2, 43, 44, 3, 2, 2, + 2, 44, 45, 5, 10, 6, 2, 45, 46, 5, 18, 10, 2, 46, 95, 3, 2, 2, 2, 47, 48, + 5, 20, 11, 2, 48, 50, 7, 26, 2, 2, 49, 51, 5, 26, 14, 2, 50, 49, 3, 2, + 2, 2, 50, 51, 3, 2, 2, 2, 51, 52, 3, 2, 2, 2, 52, 53, 7, 27, 2, 2, 53, + 95, 3, 2, 2, 2, 54, 55, 5, 20, 11, 2, 55, 57, 7, 26, 2, 2, 56, 58, 5, 26, + 14, 2, 57, 56, 3, 2, 2, 2, 57, 58, 3, 2, 2, 2, 58, 59, 3, 2, 2, 2, 59, + 60, 5, 18, 10, 2, 60, 95, 3, 2, 2, 2, 61, 63, 5, 20, 11, 2, 62, 64, 5, + 26, 14, 2, 63, 62, 3, 2, 2, 2, 63, 64, 3, 2, 2, 2, 64, 65, 3, 2, 2, 2, + 65, 66, 7, 24, 2, 2, 66, 67, 5, 18, 10, 2, 67, 68, 7, 22, 2, 2, 68, 69, + 5, 18, 10, 2, 69, 95, 3, 2, 2, 2, 70, 72, 5, 20, 11, 2, 71, 73, 5, 26, + 14, 2, 72, 71, 3, 2, 2, 2, 72, 73, 3, 2, 2, 2, 73, 74, 3, 2, 2, 2, 74, + 75, 7, 25, 2, 2, 75, 84, 7, 3, 2, 2, 76, 81, 5, 18, 10, 2, 77, 78, 7, 4, + 2, 2, 78, 80, 5, 18, 10, 2, 79, 77, 3, 2, 2, 2, 80, 83, 3, 2, 2, 2, 81, + 79, 3, 2, 2, 2, 81, 82, 3, 2, 2, 2, 82, 85, 3, 2, 2, 2, 83, 81, 3, 2, 2, + 2, 84, 76, 3, 2, 2, 2, 84, 85, 3, 2, 2, 2, 85, 86, 3, 2, 2, 2, 86, 87, + 7, 5, 2, 2, 87, 95, 3, 2, 2, 2, 88, 89, 7, 28, 2, 2, 89, 95, 5, 4, 3, 6, + 90, 91, 7, 3, 2, 2, 91, 92, 5, 4, 3, 2, 92, 93, 7, 5, 2, 2, 93, 95, 3, + 2, 2, 2, 94, 31, 3, 2, 2, 2, 94, 36, 3, 2, 2, 2, 94, 40, 3, 2, 2, 2, 94, + 47, 3, 2, 2, 2, 94, 54, 3, 2, 2, 2, 94, 61, 3, 2, 2, 2, 94, 70, 3, 2, 2, + 2, 94, 88, 3, 2, 2, 2, 94, 90, 3, 2, 2, 2, 95, 104, 3, 2, 2, 2, 96, 97, + 12, 5, 2, 2, 97, 98, 7, 22, 2, 2, 98, 103, 5, 4, 3, 6, 99, 100, 12, 4, + 2, 2, 100, 101, 7, 23, 2, 2, 101, 103, 5, 4, 3, 5, 102, 96, 3, 2, 2, 2, + 102, 99, 3, 2, 2, 2, 103, 106, 3, 2, 2, 2, 104, 102, 3, 2, 2, 2, 104, 105, + 3, 2, 2, 2, 105, 5, 3, 2, 2, 2, 106, 104, 3, 2, 2, 2, 107, 110, 9, 2, 2, + 2, 108, 110, 9, 3, 2, 2, 109, 107, 3, 2, 2, 2, 109, 108, 3, 2, 2, 2, 110, + 7, 3, 2, 2, 2, 111, 112, 9, 4, 2, 2, 112, 9, 3, 2, 2, 2, 113, 114, 9, 5, + 2, 2, 114, 11, 3, 2, 2, 2, 115, 116, 7, 29, 2, 2, 116, 13, 3, 2, 2, 2, + 117, 118, 7, 29, 2, 2, 118, 15, 3, 2, 2, 2, 119, 120, 7, 29, 2, 2, 120, + 17, 3, 2, 2, 2, 121, 124, 5, 22, 12, 2, 122, 124, 5, 24, 13, 2, 123, 121, + 3, 2, 2, 2, 123, 122, 3, 2, 2, 2, 124, 19, 3, 2, 2, 2, 125, 126, 8, 11, + 1, 2, 126, 132, 5, 16, 9, 2, 127, 128, 7, 3, 2, 2, 128, 129, 5, 20, 11, + 2, 129, 130, 7, 5, 2, 2, 130, 132, 3, 2, 2, 2, 131, 125, 3, 2, 2, 2, 131, + 127, 3, 2, 2, 2, 132, 165, 3, 2, 2, 2, 133, 134, 12, 8, 2, 2, 134, 137, + 7, 15, 2, 2, 135, 138, 5, 18, 10, 2, 136, 138, 5, 20, 11, 2, 137, 135, + 3, 2, 2, 2, 137, 136, 3, 2, 2, 2, 138, 164, 3, 2, 2, 2, 139, 140, 12, 7, + 2, 2, 140, 143, 7, 16, 2, 2, 141, 144, 5, 18, 10, 2, 142, 144, 5, 20, 11, + 2, 143, 141, 3, 2, 2, 2, 143, 142, 3, 2, 2, 2, 144, 164, 3, 2, 2, 2, 145, + 146, 12, 6, 2, 2, 146, 149, 7, 17, 2, 2, 147, 150, 5, 18, 10, 2, 148, 150, + 5, 20, 11, 2, 149, 147, 3, 2, 2, 2, 149, 148, 3, 2, 2, 2, 150, 164, 3, + 2, 2, 2, 151, 152, 12, 5, 2, 2, 152, 155, 7, 18, 2, 2, 153, 156, 5, 18, + 10, 2, 154, 156, 5, 20, 11, 2, 155, 153, 3, 2, 2, 2, 155, 154, 3, 2, 2, + 2, 156, 164, 3, 2, 2, 2, 157, 158, 12, 4, 2, 2, 158, 161, 7, 19, 2, 2, + 159, 162, 5, 18, 10, 2, 160, 162, 5, 20, 11, 2, 161, 159, 3, 2, 2, 2, 161, + 160, 3, 2, 2, 2, 162, 164, 3, 2, 2, 2, 163, 133, 3, 2, 2, 2, 163, 139, + 3, 2, 2, 2, 163, 145, 3, 2, 2, 2, 163, 151, 3, 2, 2, 2, 163, 157, 3, 2, + 2, 2, 164, 167, 3, 2, 2, 2, 165, 163, 3, 2, 2, 2, 165, 166, 3, 2, 2, 2, + 166, 21, 3, 2, 2, 2, 167, 165, 3, 2, 2, 2, 168, 170, 9, 6, 2, 2, 169, 168, + 3, 2, 2, 2, 169, 170, 3, 2, 2, 2, 170, 171, 3, 2, 2, 2, 171, 172, 7, 30, + 2, 2, 172, 23, 3, 2, 2, 2, 173, 174, 7, 31, 2, 2, 174, 25, 3, 2, 2, 2, + 175, 176, 7, 28, 2, 2, 176, 27, 3, 2, 2, 2, 23, 42, 50, 57, 63, 72, 81, + 84, 94, 102, 104, 109, 123, 131, 137, 143, 149, 155, 161, 163, 165, 169, +} +var deserializer = antlr.NewATNDeserializer(nil) +var deserializedATN = deserializer.DeserializeFromUInt16(parserATN) + +var literalNames = []string{ + "", "'('", "','", "')'", "'<'", "'<='", "'>'", "'>='", "'='", "'!='", "'<>'", + "'~='", "'~!'", "'*'", "'/'", "'%'", "'+'", "'-'", +} +var symbolicNames = []string{ + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + "K_LIKE", "K_ILIKE", "K_AND", "K_OR", "K_BETWEEN", "K_IN", "K_IS", "K_NULL", + "K_NOT", "IDENTIFIER", "NUMERIC_LITERAL", "STRING_LITERAL", "SPACES", +} + +var ruleNames = []string{ + "start", "expr", "literalOp", "stringOp", "likeOp", "databaseName", "tableName", + "columnName", "literalValue", "mathExp", "signedNumber", "stringValue", + "keyNot", +} +var decisionToDFA = make([]*antlr.DFA, len(deserializedATN.DecisionToState)) + +func init() { + for index, ds := range deserializedATN.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(ds, index) + } +} + +type TSLParser struct { + *antlr.BaseParser +} + +func NewTSLParser(input antlr.TokenStream) *TSLParser { + this := new(TSLParser) + + this.BaseParser = antlr.NewBaseParser(input) + + this.Interpreter = antlr.NewParserATNSimulator(this, deserializedATN, decisionToDFA, antlr.NewPredictionContextCache()) + this.RuleNames = ruleNames + this.LiteralNames = literalNames + this.SymbolicNames = symbolicNames + this.GrammarFileName = "TSL.g4" + + return this +} + +// TSLParser tokens. +const ( + TSLParserEOF = antlr.TokenEOF + TSLParserT__0 = 1 + TSLParserT__1 = 2 + TSLParserT__2 = 3 + TSLParserT__3 = 4 + TSLParserT__4 = 5 + TSLParserT__5 = 6 + TSLParserT__6 = 7 + TSLParserT__7 = 8 + TSLParserT__8 = 9 + TSLParserT__9 = 10 + TSLParserT__10 = 11 + TSLParserT__11 = 12 + TSLParserT__12 = 13 + TSLParserT__13 = 14 + TSLParserT__14 = 15 + TSLParserT__15 = 16 + TSLParserT__16 = 17 + TSLParserK_LIKE = 18 + TSLParserK_ILIKE = 19 + TSLParserK_AND = 20 + TSLParserK_OR = 21 + TSLParserK_BETWEEN = 22 + TSLParserK_IN = 23 + TSLParserK_IS = 24 + TSLParserK_NULL = 25 + TSLParserK_NOT = 26 + TSLParserIDENTIFIER = 27 + TSLParserNUMERIC_LITERAL = 28 + TSLParserSTRING_LITERAL = 29 + TSLParserSPACES = 30 +) + +// TSLParser rules. +const ( + TSLParserRULE_start = 0 + TSLParserRULE_expr = 1 + TSLParserRULE_literalOp = 2 + TSLParserRULE_stringOp = 3 + TSLParserRULE_likeOp = 4 + TSLParserRULE_databaseName = 5 + TSLParserRULE_tableName = 6 + TSLParserRULE_columnName = 7 + TSLParserRULE_literalValue = 8 + TSLParserRULE_mathExp = 9 + TSLParserRULE_signedNumber = 10 + TSLParserRULE_stringValue = 11 + TSLParserRULE_keyNot = 12 +) + +// IStartContext is an interface to support dynamic dispatch. +type IStartContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsStartContext differentiates from other interfaces. + IsStartContext() +} + +type StartContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyStartContext() *StartContext { + var p = new(StartContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = TSLParserRULE_start + return p +} + +func (*StartContext) IsStartContext() {} + +func NewStartContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *StartContext { + var p = new(StartContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = TSLParserRULE_start + + return p +} + +func (s *StartContext) GetParser() antlr.Parser { return s.parser } + +func (s *StartContext) Expr() IExprContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExprContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IExprContext) +} + +func (s *StartContext) EOF() antlr.TerminalNode { + return s.GetToken(TSLParserEOF, 0) +} + +func (s *StartContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *StartContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *StartContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterStart(s) + } +} + +func (s *StartContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitStart(s) + } +} + +func (p *TSLParser) Start() (localctx IStartContext) { + localctx = NewStartContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 0, TSLParserRULE_start) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(26) + p.expr(0) + } + { + p.SetState(27) + p.Match(TSLParserEOF) + } + + return localctx +} + +// IExprContext is an interface to support dynamic dispatch. +type IExprContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsExprContext differentiates from other interfaces. + IsExprContext() +} + +type ExprContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyExprContext() *ExprContext { + var p = new(ExprContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = TSLParserRULE_expr + return p +} + +func (*ExprContext) IsExprContext() {} + +func NewExprContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExprContext { + var p = new(ExprContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = TSLParserRULE_expr + + return p +} + +func (s *ExprContext) GetParser() antlr.Parser { return s.parser } + +func (s *ExprContext) CopyFrom(ctx *ExprContext) { + s.BaseParserRuleContext.CopyFrom(ctx.BaseParserRuleContext) +} + +func (s *ExprContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ExprContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +type ParContext struct { + *ExprContext +} + +func NewParContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ParContext { + var p = new(ParContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *ParContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ParContext) Expr() IExprContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExprContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IExprContext) +} + +func (s *ParContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterPar(s) + } +} + +func (s *ParContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitPar(s) + } +} + +type NotContext struct { + *ExprContext +} + +func NewNotContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *NotContext { + var p = new(NotContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *NotContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *NotContext) K_NOT() antlr.TerminalNode { + return s.GetToken(TSLParserK_NOT, 0) +} + +func (s *NotContext) Expr() IExprContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExprContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IExprContext) +} + +func (s *NotContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterNot(s) + } +} + +func (s *NotContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitNot(s) + } +} + +type LikeContext struct { + *ExprContext +} + +func NewLikeContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *LikeContext { + var p = new(LikeContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *LikeContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *LikeContext) MathExp() IMathExpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IMathExpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IMathExpContext) +} + +func (s *LikeContext) LikeOp() ILikeOpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ILikeOpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ILikeOpContext) +} + +func (s *LikeContext) LiteralValue() ILiteralValueContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ILiteralValueContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ILiteralValueContext) +} + +func (s *LikeContext) KeyNot() IKeyNotContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IKeyNotContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IKeyNotContext) +} + +func (s *LikeContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterLike(s) + } +} + +func (s *LikeContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitLike(s) + } +} + +type OrContext struct { + *ExprContext +} + +func NewOrContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *OrContext { + var p = new(OrContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *OrContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *OrContext) AllExpr() []IExprContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IExprContext)(nil)).Elem()) + var tst = make([]IExprContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IExprContext) + } + } + + return tst +} + +func (s *OrContext) Expr(i int) IExprContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExprContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IExprContext) +} + +func (s *OrContext) K_OR() antlr.TerminalNode { + return s.GetToken(TSLParserK_OR, 0) +} + +func (s *OrContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterOr(s) + } +} + +func (s *OrContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitOr(s) + } +} + +type InContext struct { + *ExprContext +} + +func NewInContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *InContext { + var p = new(InContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *InContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *InContext) MathExp() IMathExpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IMathExpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IMathExpContext) +} + +func (s *InContext) K_IN() antlr.TerminalNode { + return s.GetToken(TSLParserK_IN, 0) +} + +func (s *InContext) KeyNot() IKeyNotContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IKeyNotContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IKeyNotContext) +} + +func (s *InContext) AllLiteralValue() []ILiteralValueContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*ILiteralValueContext)(nil)).Elem()) + var tst = make([]ILiteralValueContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(ILiteralValueContext) + } + } + + return tst +} + +func (s *InContext) LiteralValue(i int) ILiteralValueContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ILiteralValueContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(ILiteralValueContext) +} + +func (s *InContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterIn(s) + } +} + +func (s *InContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitIn(s) + } +} + +type IsLiteralContext struct { + *ExprContext +} + +func NewIsLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IsLiteralContext { + var p = new(IsLiteralContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *IsLiteralContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IsLiteralContext) MathExp() IMathExpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IMathExpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IMathExpContext) +} + +func (s *IsLiteralContext) K_IS() antlr.TerminalNode { + return s.GetToken(TSLParserK_IS, 0) +} + +func (s *IsLiteralContext) LiteralValue() ILiteralValueContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ILiteralValueContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ILiteralValueContext) +} + +func (s *IsLiteralContext) KeyNot() IKeyNotContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IKeyNotContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IKeyNotContext) +} + +func (s *IsLiteralContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterIsLiteral(s) + } +} + +func (s *IsLiteralContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitIsLiteral(s) + } +} + +type AndContext struct { + *ExprContext +} + +func NewAndContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *AndContext { + var p = new(AndContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *AndContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *AndContext) AllExpr() []IExprContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IExprContext)(nil)).Elem()) + var tst = make([]IExprContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IExprContext) + } + } + + return tst +} + +func (s *AndContext) Expr(i int) IExprContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IExprContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IExprContext) +} + +func (s *AndContext) K_AND() antlr.TerminalNode { + return s.GetToken(TSLParserK_AND, 0) +} + +func (s *AndContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterAnd(s) + } +} + +func (s *AndContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitAnd(s) + } +} + +type BetweenContext struct { + *ExprContext +} + +func NewBetweenContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *BetweenContext { + var p = new(BetweenContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *BetweenContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *BetweenContext) MathExp() IMathExpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IMathExpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IMathExpContext) +} + +func (s *BetweenContext) K_BETWEEN() antlr.TerminalNode { + return s.GetToken(TSLParserK_BETWEEN, 0) +} + +func (s *BetweenContext) AllLiteralValue() []ILiteralValueContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*ILiteralValueContext)(nil)).Elem()) + var tst = make([]ILiteralValueContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(ILiteralValueContext) + } + } + + return tst +} + +func (s *BetweenContext) LiteralValue(i int) ILiteralValueContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ILiteralValueContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(ILiteralValueContext) +} + +func (s *BetweenContext) K_AND() antlr.TerminalNode { + return s.GetToken(TSLParserK_AND, 0) +} + +func (s *BetweenContext) KeyNot() IKeyNotContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IKeyNotContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IKeyNotContext) +} + +func (s *BetweenContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterBetween(s) + } +} + +func (s *BetweenContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitBetween(s) + } +} + +type StringOpsContext struct { + *ExprContext +} + +func NewStringOpsContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *StringOpsContext { + var p = new(StringOpsContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *StringOpsContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *StringOpsContext) MathExp() IMathExpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IMathExpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IMathExpContext) +} + +func (s *StringOpsContext) StringOp() IStringOpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IStringOpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IStringOpContext) +} + +func (s *StringOpsContext) LiteralValue() ILiteralValueContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ILiteralValueContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ILiteralValueContext) +} + +func (s *StringOpsContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterStringOps(s) + } +} + +func (s *StringOpsContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitStringOps(s) + } +} + +type IsNullContext struct { + *ExprContext +} + +func NewIsNullContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IsNullContext { + var p = new(IsNullContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *IsNullContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IsNullContext) MathExp() IMathExpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IMathExpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IMathExpContext) +} + +func (s *IsNullContext) K_IS() antlr.TerminalNode { + return s.GetToken(TSLParserK_IS, 0) +} + +func (s *IsNullContext) K_NULL() antlr.TerminalNode { + return s.GetToken(TSLParserK_NULL, 0) +} + +func (s *IsNullContext) KeyNot() IKeyNotContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IKeyNotContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IKeyNotContext) +} + +func (s *IsNullContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterIsNull(s) + } +} + +func (s *IsNullContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitIsNull(s) + } +} + +type LiteralOpsContext struct { + *ExprContext +} + +func NewLiteralOpsContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *LiteralOpsContext { + var p = new(LiteralOpsContext) + + p.ExprContext = NewEmptyExprContext() + p.parser = parser + p.CopyFrom(ctx.(*ExprContext)) + + return p +} + +func (s *LiteralOpsContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *LiteralOpsContext) MathExp() IMathExpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IMathExpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IMathExpContext) +} + +func (s *LiteralOpsContext) LiteralOp() ILiteralOpContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ILiteralOpContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ILiteralOpContext) +} + +func (s *LiteralOpsContext) LiteralValue() ILiteralValueContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ILiteralValueContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ILiteralValueContext) +} + +func (s *LiteralOpsContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.EnterLiteralOps(s) + } +} + +func (s *LiteralOpsContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(TSLListener); ok { + listenerT.ExitLiteralOps(s) + } +} + +func (p *TSLParser) Expr() (localctx IExprContext) { + return p.expr(0) +} + +func (p *TSLParser) expr(_p int) (localctx IExprContext) { + var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext() + _parentState := p.GetState() + localctx = NewExprContext(p, p.GetParserRuleContext(), _parentState) + var _prevctx IExprContext = localctx + var _ antlr.ParserRuleContext = _prevctx // TODO: To prevent unused variable warning. + _startState := 2 + p.EnterRecursionRule(localctx, 2, TSLParserRULE_expr, _p) + var _la int + + defer func() { + p.UnrollRecursionContexts(_parentctx) + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + var _alt int + + p.EnterOuterAlt(localctx, 1) + p.SetState(92) + p.GetErrorHandler().Sync(p) + switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 7, p.GetParserRuleContext()) { + case 1: + localctx = NewLiteralOpsContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + + { + p.SetState(30) + p.mathExp(0) + } + { + p.SetState(31) + p.LiteralOp() + } + { + p.SetState(32) + p.LiteralValue() + } + + case 2: + localctx = NewStringOpsContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + { + p.SetState(34) + p.mathExp(0) + } + { + p.SetState(35) + p.StringOp() + } + { + p.SetState(36) + p.LiteralValue() + } + + case 3: + localctx = NewLikeContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + { + p.SetState(38) + p.mathExp(0) + } + p.SetState(40) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == TSLParserK_NOT { + { + p.SetState(39) + p.KeyNot() + } + + } + { + p.SetState(42) + p.LikeOp() + } + { + p.SetState(43) + p.LiteralValue() + } + + case 4: + localctx = NewIsNullContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + { + p.SetState(45) + p.mathExp(0) + } + { + p.SetState(46) + p.Match(TSLParserK_IS) + } + p.SetState(48) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == TSLParserK_NOT { + { + p.SetState(47) + p.KeyNot() + } + + } + { + p.SetState(50) + p.Match(TSLParserK_NULL) + } + + case 5: + localctx = NewIsLiteralContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + { + p.SetState(52) + p.mathExp(0) + } + { + p.SetState(53) + p.Match(TSLParserK_IS) + } + p.SetState(55) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == TSLParserK_NOT { + { + p.SetState(54) + p.KeyNot() + } + + } + { + p.SetState(57) + p.LiteralValue() + } + + case 6: + localctx = NewBetweenContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + { + p.SetState(59) + p.mathExp(0) + } + p.SetState(61) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == TSLParserK_NOT { + { + p.SetState(60) + p.KeyNot() + } + + } + { + p.SetState(63) + p.Match(TSLParserK_BETWEEN) + } + { + p.SetState(64) + p.LiteralValue() + } + { + p.SetState(65) + p.Match(TSLParserK_AND) + } + { + p.SetState(66) + p.LiteralValue() + } + + case 7: + localctx = NewInContext(p, localctx) + p.SetParserRuleContext(localctx) + _prevctx = localctx + { + p.SetState(68) + p.mathExp(0) + } + p.SetState(70) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == TSLParserK_NOT { + { + p.SetState(69) + p.KeyNot() + } + + } + { + p.SetState(72) + p.Match(TSLParserK_IN) + } + + { + p.SetState(73) + p.Match(TSLParserT__0) + } + p.SetState(82) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if ((_la)&-(0x1f+1)) == 0 && ((1< +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tsl + +// TLS operators. +const ( + IdentOp = "$ident" // Empty operator for itentifiers + ArrayOp = "$array" // Empty operator for arrays + StringOp = "$string" // Empty operator for strings + NumberOp = "$number" // Empty operator for numbers + NullOp = "$null" // Empty operator for nulls + LtOp = "$lt" + LteOp = "$lte" + GtOp = "$gt" + GteOp = "$gte" + EqOp = "$eq" + NotEqOp = "$ne" + RegexOp = "$regex" + NotRegexOp = "$nregex" + LikeOp = "$like" + ILikeOp = "$ilike" + InOp = "$in" + NotInOp = "$nin" + BetweenOp = "$between" + NotBetweenOp = "$nbetween" + NotOp = "$not" + AndOp = "$and" + OrOp = "$or" + IsNilOp = "$nexists" + IsNotNilOp = "$exists" + AddOp = "$add" + SubtractOp = "$subtract" + MultiplyOp = "$multiply" + DivideOp = "$divide" + ModuloOp = "$modulo" +) + +// opDic maps SQL'ish operators to TLS operators. +var opDic = map[string]string{ + "<": LtOp, + "<=": LteOp, + ">": GtOp, + ">=": GteOp, + "=": EqOp, + "!=": NotEqOp, + "<>": NotEqOp, + "~=": RegexOp, + "~!": NotRegexOp, + "+": AddOp, + "-": SubtractOp, + "*": MultiplyOp, + "/": DivideOp, + "%": ModuloOp, +} diff --git a/v5/pkg/tsl/doc.go b/v5/pkg/tsl/doc.go new file mode 100644 index 0000000..71e3c4a --- /dev/null +++ b/v5/pkg/tsl/doc.go @@ -0,0 +1,38 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package tsl describe and parse the Tree Search Language (TSL), +// TSL is a wonderful search language, With similar grammar to SQL's +// where part. +// +// TSL grammar examples: +// "name = 'joe' or name = 'jane'" +// "city in ('paris', 'rome', 'milan') and state != 'spane'" +// "pages between 100 and 200 and author.name ~= 'Hilbert'" +// +// The package provide the ParseTSL method to convert TSL string into TSL tree. +// +// Usage: +// // Parse input string into a TSL tree. +// tree, err := tsl.ParseTSL("user.name like 'Joe'") +// if err != nil { +// log.Fatal(err) +// } +// +// TSL tree can be used to generate SQL and MongoDB query filters, see the walkers +// package for TSL tree walking examples. +// +// https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers +package tsl diff --git a/v5/pkg/tsl/error_listener.go b/v5/pkg/tsl/error_listener.go new file mode 100644 index 0000000..1c4af57 --- /dev/null +++ b/v5/pkg/tsl/error_listener.go @@ -0,0 +1,42 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tsl + +import ( + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +// ErrorListener an error listener for antlr parser. +type ErrorListener struct { + *antlr.DefaultErrorListener + Err error +} + +// NewErrorListener create a new error listener. +func NewErrorListener() *ErrorListener { + return new(ErrorListener) +} + +// SyntaxError handle an error. +func (d *ErrorListener) SyntaxError( + recognizer antlr.Recognizer, + offendingSymbol interface{}, + line, column int, + msg string, + e antlr.RecognitionException) { + + d.Err = ParseError{line: line, column: column, msg: msg} +} diff --git a/v5/pkg/tsl/errors.go b/v5/pkg/tsl/errors.go new file mode 100644 index 0000000..733cafb --- /dev/null +++ b/v5/pkg/tsl/errors.go @@ -0,0 +1,57 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tsl + +import "fmt" + +// UnexpectedLiteralError is raised when an unexpected literal is found. +type UnexpectedLiteralError struct { + ExpectedType string // the expected literal type. + Literal interface{} // the literal found. +} + +func (e UnexpectedLiteralError) Error() string { + // if no literal is given then it's a missing literal error. + if e.Literal == "" { + return fmt.Sprintf("unexpected missing literal") + } + + // If no type is given it's an unexpected literal error. + if e.ExpectedType == "" { + return fmt.Sprintf("unexpected literal: %v", e.Literal) + } + + // o/w it's an unexpected type of literal error. + return fmt.Sprintf("expected a %s literal, found: %v", e.ExpectedType, e.Literal) +} + +// StackError is raised when the parser stack has unexpected size. +type StackError struct{} + +func (e StackError) Error() string { + return fmt.Sprintf("unexpected operator stack") +} + +// ParseError is raised on parser error. +type ParseError struct { + line int + column int + msg string +} + +func (e ParseError) Error() string { + return fmt.Sprintf("parse error [%d:%d]: %s", e.line, e.column, e.msg) +} diff --git a/v5/pkg/tsl/listener.go b/v5/pkg/tsl/listener.go new file mode 100644 index 0000000..1ae875e --- /dev/null +++ b/v5/pkg/tsl/listener.go @@ -0,0 +1,346 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tsl + +import ( + "fmt" + "strconv" + "strings" + + "github.com/yaacov/tree-search-language/v5/pkg/parser" +) + +// Listener is a Tree search listener. +type Listener struct { + *parser.BaseTSLListener + + Stack []Node + Errs []error +} + +// GetTree return the parsed tree, if exist. +func (l *Listener) GetTree() (n Node, err error) { + // Check for errors. + if len(l.Errs) > 0 { + err = l.Errs[0] + return + } + + // Check stack size. + if len(l.Stack) != 1 { + err = StackError{} + return + } + + n = l.Stack[0] + return +} + +// ExitColumnIdentifier is called when exiting the ColumnIdentifier production. +func (l *Listener) ExitColumnIdentifier(c *parser.ColumnIdentifierContext) { + l.exitLiteral(IdentOp, c.ColumnName().GetText()) +} + +// ExitNumberLiteral is called when exiting the NumberLiteral production. +func (l *Listener) ExitNumberLiteral(c *parser.NumberLiteralContext) { + // Check for a float value. + f, err := strconv.ParseFloat(c.SignedNumber().GetText(), 64) + if err != nil { + l.Errs = append(l.Errs, err) + } + + l.exitLiteral(NumberOp, f) +} + +// ExitStringLiteral is called when exiting the StringLiteral production. +func (l *Listener) ExitStringLiteral(c *parser.StringLiteralContext) { + // StringValue must be a string of format \'.*'\, + // length must be greater or equal to 2. + s := c.StringValue().GetText() + ln := len(s) + v := strings.Replace(s[1:ln-1], "''", "'", -1) + + l.exitLiteral(StringOp, v) +} + +// ExitMulOps is called when production multiply op is exited. +func (l *Listener) ExitMulOps(c *parser.MulOpsContext) { + l.exitMathOps(MultiplyOp) +} + +// ExitDivOps is called when production div op is exited. +func (l *Listener) ExitDivOps(c *parser.DivOpsContext) { + l.exitMathOps(DivideOp) +} + +// ExitModOps is called when production modulo op is exited. +func (l *Listener) ExitModOps(c *parser.ModOpsContext) { + l.exitMathOps(ModuloOp) +} + +// ExitAddOps is called when production add op is exited. +func (l *Listener) ExitAddOps(c *parser.AddOpsContext) { + l.exitMathOps(AddOp) +} + +// ExitSubOps is called when production subtract op is exited. +func (l *Listener) ExitSubOps(c *parser.SubOpsContext) { + l.exitMathOps(SubtractOp) +} + +// ExitLiteralOps is called when production LiteralOps is exited. +func (l *Listener) ExitLiteralOps(c *parser.LiteralOpsContext) { + right, left := l.pop(), l.pop() + n := Node{ + Func: opDic[c.LiteralOp().GetText()], + Left: left, + Right: right, + } + + l.push(n) +} + +// ExitStringOps is called when production StringOps is exited. +func (l *Listener) ExitStringOps(c *parser.StringOpsContext) { + right, left := l.pop(), l.pop() + + // Check right op is a string. + if right.Func != StringOp { + l.Errs = append(l.Errs, UnexpectedLiteralError{ExpectedType: "string", Literal: right.Left}) + return + } + + n := Node{ + Func: opDic[c.StringOp().GetText()], + Left: left, + Right: right, + } + + l.push(n) +} + +// ExitLike is called when production Like is exited. +func (l *Listener) ExitLike(c *parser.LikeContext) { + right, left := l.pop(), l.pop() + + // Check the operator to use according to the given LIKE or ILIKE keyword: + var op string + switch c.LikeOp().GetStart().GetTokenType() { + case parser.TSLLexerK_LIKE: + op = LikeOp + case parser.TSLLexerK_ILIKE: + op = ILikeOp + default: + // This will never happen, as the grammar doesn't allow it, however this will catch + // errors if in the future we add new operators to the grammar but we forget to + // update this code. + l.Errs = append(l.Errs, fmt.Errorf("expected LIKE or ILIKE")) + return + } + + // Check right op is a string. + if right.Func != StringOp { + l.Errs = append(l.Errs, UnexpectedLiteralError{ExpectedType: "string", Literal: right.Left}) + return + } + + // Create the node for the LIKE or ILIKE operation: + n := Node{ + Func: op, + Left: left, + Right: right, + } + + // If there is a NOT before the LIKE or ILIKE operations then create an + // additional node for the negation: + if c.KeyNot() != nil { + n = Node{ + Func: NotOp, + Left: n, + } + } + + l.push(n) +} + +// ExitIsLiteral is called when production IsLiteral is exited. +func (l *Listener) ExitIsLiteral(c *parser.IsLiteralContext) { + right, left := l.pop(), l.pop() + op := ternaryOp(c.KeyNot() == nil, EqOp, NotEqOp) + + n := Node{ + Func: op, + Left: left, + Right: right, + } + + l.push(n) +} + +// ExitIsNull is called when production IsNull is exited. +func (l *Listener) ExitIsNull(c *parser.IsNullContext) { + left := l.pop() + op := ternaryOp(c.KeyNot() == nil, IsNilOp, IsNotNilOp) + + n := Node{ + Func: op, + Left: left, + } + + l.push(n) +} + +// ExitIn is called when production In is exited. +func (l *Listener) ExitIn(c *parser.InContext) { + right := Node{ + Func: ArrayOp, + Right: l.popLiterals([]Node{}), + } + left := l.pop() + op := ternaryOp(c.KeyNot() == nil, InOp, NotInOp) + + n := Node{ + Func: op, + Left: left, + Right: right, + } + + l.push(n) +} + +// ExitBetween is called when production Between is exited. +func (l *Listener) ExitBetween(c *parser.BetweenContext) { + nodes := []Node{l.pop(), l.pop()} + right := Node{ + Func: ArrayOp, + Right: []Node{nodes[1], nodes[0]}, + } + + left := l.pop() + op := ternaryOp(c.KeyNot() == nil, BetweenOp, NotBetweenOp) + + n := Node{ + Func: op, + Left: left, + Right: right, + } + + l.push(n) +} + +// ExitNot is called when production Not is exited. +func (l *Listener) ExitNot(c *parser.NotContext) { + left := l.pop() + n := Node{ + Func: NotOp, + Left: left, + } + + l.push(n) +} + +// ExitAnd is called when production And is exited. +func (l *Listener) ExitAnd(c *parser.AndContext) { + right, left := l.pop(), l.pop() + n := Node{ + Func: AndOp, + Left: left, + Right: right, + } + + l.push(n) +} + +// ExitOr is called when production Or is exited. +func (l *Listener) ExitOr(c *parser.OrContext) { + right, left := l.pop(), l.pop() + n := Node{ + Func: OrOp, + Left: left, + Right: right, + } + + l.push(n) +} + +// exitMathOps is called when production math operator is exited. +func (l *Listener) exitMathOps(op string) { + right, left := l.pop(), l.pop() + + // Check right op is not a string. + if right.Func == StringOp { + l.Errs = append(l.Errs, UnexpectedLiteralError{ExpectedType: "float", Literal: right.Left}) + return + } + + n := Node{ + Func: op, + Left: left, + Right: right, + } + + l.push(n) +} + +// ExitColumnIdentifier is called when exiting a literal production. +func (l *Listener) exitLiteral(f string, v interface{}) { + n := Node{Func: f, Left: v} + l.push(n) +} + +// ternaryOp return lh if conditional is true, rh o/w. +func ternaryOp(conditional bool, lh string, rh string) string { + if conditional { + return lh + } + + return rh +} + +// popLiterals collect literal values, and create args list. +func (l *Listener) popLiterals(in []Node) (out []Node) { + // Get a literal from stack. + p := l.pop() + + // Run recurtion on literals. + if p.Func == StringOp || p.Func == NumberOp { + return l.popLiterals(append(in, p)) + } + + // If p is not a literal, add it back to stack and exit. + l.push(p) + return in +} + +// push is a helper function for pushing new node to the listener Stack. +func (l *Listener) push(i Node) { + l.Stack = append(l.Stack, i) +} + +// pop is a helper function for poping a node from the listener Stack. +func (l *Listener) pop() (n Node) { + // Check that we have nodes in the stack. + size := len(l.Stack) + if size < 1 { + l.Errs = append(l.Errs, StackError{}) + return + } + + // Pop the last value from the Stack. + n, l.Stack = l.Stack[size-1], l.Stack[:size-1] + + return +} diff --git a/v5/pkg/tsl/node.go b/v5/pkg/tsl/node.go new file mode 100644 index 0000000..82fb623 --- /dev/null +++ b/v5/pkg/tsl/node.go @@ -0,0 +1,23 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tsl + +// Node is a Tree search node. +type Node struct { + Func string `json:"func"` + Left interface{} `json:"left,omitempty"` + Right interface{} `json:"right,omitempty"` +} diff --git a/v5/pkg/tsl/tsl.go b/v5/pkg/tsl/tsl.go new file mode 100644 index 0000000..c882193 --- /dev/null +++ b/v5/pkg/tsl/tsl.go @@ -0,0 +1,57 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tsl + +import ( + "github.com/antlr/antlr4/runtime/Go/antlr" + + "github.com/yaacov/tree-search-language/v5/pkg/parser" +) + +// ParseTSL parses the input string into TSL tree. +func ParseTSL(input string) (tree Node, err error) { + // Setup the ErrorListener. + errorListener := NewErrorListener() + + // Setup the input. + is := antlr.NewInputStream(input) + + // Create the Lexer. + lexer := parser.NewTSLLexer(is) + lexer.RemoveErrorListeners() + lexer.AddErrorListener(errorListener) + stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) + + // Create the Parser. + p := parser.NewTSLParser(stream) + p.RemoveErrorListeners() + p.AddErrorListener(errorListener) + + // Parse the expression (by walking the tree). + var listener Listener + antlr.ParseTreeWalkerDefault.Walk(&listener, p.Start()) + + // Check for errors. + err = errorListener.Err + if err != nil { + return + } + + // Get the parsed tree. + tree, err = listener.GetTree() + + return +} diff --git a/v5/pkg/tsl/tsl_test.go b/v5/pkg/tsl/tsl_test.go new file mode 100644 index 0000000..4b28053 --- /dev/null +++ b/v5/pkg/tsl/tsl_test.go @@ -0,0 +1,215 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tsl + +import ( + "encoding/json" + "testing" + + "github.com/antlr/antlr4/runtime/Go/antlr" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" + + "github.com/yaacov/tree-search-language/v5/pkg/parser" +) + +func TestParser(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "TSL") +} + +var _ = Describe("Parser", func() { + DescribeTable("Returns the expected tree", + func(input, expected string) { + // Parse the input: + tree, err := parseTSL(input) + Expect(err).ToNot(HaveOccurred()) + + // Convert the tree to JSON: + actual, err := json.Marshal(tree) + Expect(err).ToNot(HaveOccurred()) + + // Check that the actual JSON and the expected JSON are structurally + // identical: + Expect(actual).To(MatchJSON(expected)) + }, + + Entry( + "Simple comparison", + "a = 'hello'", + `{ + "func": "$eq", + "left": { + "func": "$ident", + "left": "a" + }, + "right": { + "func": "$string", + "left": "hello" + } + }`, + ), + + Entry( + "Order of operators", + "a = 12.3e1 or b = 'world' and c = 'hello'", + `{ + "func": "$or", + "left": { + "func": "$eq", + "left": { + "func": "$ident", + "left": "a" + }, + "right": { + "func": "$number", + "left": 123 + } + }, + "right": { + "func": "$and", + "left": { + "func": "$eq", + "left": { + "func": "$ident", + "left": "b" + }, + "right": { + "func": "$string", + "left": "world" + } + }, + "right": { + "func": "$eq", + "left": { + "func": "$ident", + "left": "c" + }, + "right": { + "func": "$string", + "left":"hello" + } + } + } + }`, + ), + + Entry( + "Simple use of 'like'", + "a like 'my%'", + `{ + "func": "$like", + "left": { + "func": "$ident", + "left": "a" + }, + "right": { + "func": "$string", + "left": "my%" + } + }`, + ), + + Entry( + "Simple use of 'ilike'", + "a ilike 'my%'", + `{ + "func": "$ilike", + "left": { + "func": "$ident", + "left": "a" + }, + "right": { + "func": "$string", + "left": "my%" + } + }`, + ), + + Entry( + "Use of 'not' with 'like'", + "a not like 'my%'", + `{ + "func": "$not", + "left": { + "func": "$like", + "left": { + "func": "$ident", + "left": "a" + }, + "right": { + "func": "$string", + "left": "my%" + } + } + }`, + ), + + Entry( + "Use of 'not' with 'ilike'", + "a not ilike 'my%'", + `{ + "func": "$not", + "left": { + "func": "$ilike", + "left": { + "func": "$ident", + "left": "a" + }, + "right": { + "func": "$string", + "left": "my%" + } + } + }`, + ), + ) +}) + +// parseTSL parse the TSL. +func parseTSL(input string) (n Node, err error) { + // Setup the TSL ErrorListener. + errorListener := NewErrorListener() + + // Setup the input. + is := antlr.NewInputStream(input) + + // Create the Lexer + lexer := parser.NewTSLLexer(is) + lexer.RemoveErrorListeners() + lexer.AddErrorListener(errorListener) + stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) + + // Create the Parser. + p := parser.NewTSLParser(stream) + p.RemoveErrorListeners() + p.AddErrorListener(errorListener) + + // Finally parse the expression (by walking the tree). + var listener Listener + antlr.ParseTreeWalkerDefault.Walk(&listener, p.Start()) + + err = errorListener.Err + if err != nil { + return + } + + n, err = listener.GetTree() + + return +} diff --git a/v5/pkg/walkers/README.md b/v5/pkg/walkers/README.md new file mode 100644 index 0000000..cb6fb2d --- /dev/null +++ b/v5/pkg/walkers/README.md @@ -0,0 +1,27 @@ + + +

+ TSL Logo +

+ +# Tree Search Language (TSL) Imperial Walkers (IW) + +##### sql + +The `sql` package include a helper `sql.Walk` ([code](/pkg/walkers/sql/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/sql#Walk)) method that adds search to [squirrel](https://github.com/Masterminds/squirrel)'s `SelectBuilder` object. + +##### mongo + +The `mongo` package include a helper `mongo.Walk` ([code](/pkg/walkers/mongo/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/mongo#Walk)) method that adds search `bson` filter to [mongo-go-driver](https://go.mongodb.org/mongo-driver). + +##### graphviz + +The `graphviz` package include a helper `graphviz.Walk` ([code](/pkg/walkers/graphviz/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/graphviz#Walk)) method that exports `.dot` file nodes. + +##### ident + +The `ident` package include a helper `ident.Walk` ([code](/pkg/walkers/ident/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/ident#Walk)) method that checks and mapps identifier names. + +##### semantics + +The `semantics` package include a helper `semantics.Walk` ([code](/pkg/walkers/semantics/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v5/pkg/walkers/semantics#Walk)) method that reduce one data record to a bolean value (`true` or `false`) using a `tsl tree`. diff --git a/v5/pkg/walkers/doc.go b/v5/pkg/walkers/doc.go new file mode 100644 index 0000000..1c2cc7e --- /dev/null +++ b/v5/pkg/walkers/doc.go @@ -0,0 +1,45 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package walkers examples TSL tree walking, and helps to generate SQL and MongoDB +// query filters. sql.Walk and mongo.Walk methods can be used to create such filters. +// +// Squirrel walk code: https://github.com/yaacov/tree-search-language/blob/master/v5/pkg/walkers/sql/walk.go +// +// SQL generation example: https://github.com/yaacov/tree-search-language/blob/master/v5/cmd/tsl_sqlite/main.go +// +// Usage: +// filter, err := sql.Walk(tree) +// +// sql, args, err := sq.Select("name, city, state"). +// From("users"). +// Where(filter). +// ToSql() +// +// BSON walk code: https://github.com/yaacov/tree-search-language/blob/master/v5/pkg/walkers/mongo/walk.go +// +// BSON generation example: https://github.com/yaacov/tree-search-language/blob/master/v5/cmd/tsl_mongo/main.go +// +// Usage: +// // Prepare a bson filter +// filter, err = mongo.Walk(tree) +// +// // Run query +// cur, err := collection.Find(ctx, filter) +// +// squirrel: https://github.com/Masterminds/squirrel +// +// mongo-go-driver: https://go.mongodb.org/mongo-driver +package walkers diff --git a/v5/pkg/walkers/graphviz/walk.go b/v5/pkg/walkers/graphviz/walk.go new file mode 100644 index 0000000..8e86aec --- /dev/null +++ b/v5/pkg/walkers/graphviz/walk.go @@ -0,0 +1,153 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package graphviz helps to create graphviz dot files using the TSL package. +package graphviz + +import ( + "fmt" + "math/rand" + "strings" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" +) + +// Character poll for random string genrator. +const pool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +// Default styles for .dot file output. +const identStyle = "shape=record color=red" +const numberStyle = "shape=record color=blue" +const stringStyle = "shape=record color=blue" +const opStyle = "shape=box color=black" + +// Generate a random string. +func randStr(l int) string { + bytes := make([]byte, l) + for i := 0; i < l; i++ { + bytes[i] = pool[rand.Intn(len(pool))] + } + return string(bytes) +} + +// Walk travel the TSL tree to create Graphviz dot nodes. +// +// Users can call the Walk method to get the dot file graph string. +// +// s, err = graphviz.Walk("", tree, "") +// +// The return string will be a node list for a .dot file: +// +// tcuA [shape=box color=black label="$eq"] +// xhxK [shape=record color=red label="$ident | 'city'" ] +// QFDa [shape=record color=blue label="$string | 'rome'" ] +// tcuA -> { xhxK, QFDa } +// +// For valid Graphviz dot file, the nodes must be wrapped in a `digraph` object: +// +// s, err = graphviz.Walk("", tree, "") +// s = fmt.Sprintf("digraph {\n%s\n}\n", s) +func Walk(in string, n tsl.Node, nodeID string) (out string, err error) { + // If node ID is missing, create one. + if nodeID == "" { + nodeID = randStr(4) + } + + // If we have in string, add a new line break. + if in != "" { + in = fmt.Sprintf("%s\n", in) + } + + switch n.Func { + case tsl.IdentOp: + // Add leaf label and value. + nodeLabel := fmt.Sprintf("%s [%s label=\"%s | '%s'\" ]", + nodeID, + identStyle, + n.Func, + n.Left) + out = fmt.Sprintf("%s%s", in, nodeLabel) + case tsl.StringOp: + // Add leaf label and value. + nodeLabel := fmt.Sprintf("%s [%s label=\"%s | '%s'\" ]", + nodeID, + stringStyle, + n.Func, + n.Left) + out = fmt.Sprintf("%s%s", in, nodeLabel) + case tsl.NumberOp: + // Add leaf label and value. + nodeLabel := fmt.Sprintf("%s [%s label=\"%s | %g\" ]", + nodeID, + numberStyle, + n.Func, + n.Left) + out = fmt.Sprintf("%s%s", in, nodeLabel) + default: + // Add node label. + st := fmt.Sprintf("%s [%s label=\"%s\"]", + nodeID, + opStyle, + n.Func) + childrens := []string{} + + // Add left child. + if n.Left != nil { + leftID := randStr(4) + childrens = append(childrens, leftID) + + l, err := Walk(in, n.Left.(tsl.Node), leftID) + if err != nil { + return "", err + } + st = fmt.Sprintf("%s\n%s", st, l) + } + + // Add right child. + if n.Right != nil { + var nn []tsl.Node + + // Check if right hand arg is a node, or an array of nodes. + if n.Func == tsl.ArrayOp { + nn = n.Right.([]tsl.Node) + } else { + nn = []tsl.Node{n.Right.(tsl.Node)} + } + + // Add all nodes to graph. + for _, v := range nn { + rightID := randStr(4) + childrens = append(childrens, rightID) + + r, err := Walk(in, v, rightID) + if err != nil { + return "", err + } + st = fmt.Sprintf("%s\n%s", st, r) + } + } + + // Add parrent node. + in = fmt.Sprintf("%s%s\n%s -> { %s }", + in, + st, + nodeID, + strings.Join(childrens, ", ")) + + return in, nil + } + + return +} diff --git a/v5/pkg/walkers/ident/walk.go b/v5/pkg/walkers/ident/walk.go new file mode 100644 index 0000000..c54be01 --- /dev/null +++ b/v5/pkg/walkers/ident/walk.go @@ -0,0 +1,97 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package ident helps to replace identifiers in a TSL tree. +package ident + +import ( + "github.com/yaacov/tree-search-language/v5/pkg/tsl" +) + +// Walk travel the TSL tree and replace identifiers. +// +// Users can call the Walk method to check and replace identifiers. +// +// Example: +// columnNamesMap := map[string]string{ +// "title": "title", +// "author": "author", +// "spec.pages": "pages", +// "spec.rating": "rating", +// } +// +// func check(s string) (string, error) { +// // Chekc for column name in map. +// if v, ok := columnNamesMap[s]; ok { +// return v, nil +// } +// +// // If not found return string as is, and an error. +// return s, fmt.Errorf("column not found") +// } +// +// // Check and replace user identifiers with the SQL table column names. +// // +// // SQL table columns are "title, author, pages and rating", but for +// // users pages and ratings are fields of an internal struct called +// // spec (e.g. {"title": "Book", "author": "Joe", "spec": {"pages": 5, "rating": 5}}). +// // +// newTree, err = ident.Walk(tree, check) +// +func Walk(n tsl.Node, checkColumnName func(string) (string, error)) (tsl.Node, error) { + var err error + var v string + + // Walk tree. + switch n.Func { + case tsl.IdentOp: + // If we have an identifier, check for it in the identMap. + if v, err = checkColumnName(n.Left.(string)); err == nil { + // If valid identifier, use it. + n.Left = v + return n, nil + } + + return n, err + case tsl.StringOp, tsl.NumberOp: + // This are our leafs. + return n, nil + default: + // Check identifiers on left side. + if n.Left != nil { + n.Left, err = Walk(n.Left.(tsl.Node), checkColumnName) + if err != nil { + return n, err + } + } + + // Check identifiers on right side. + if n.Right != nil { + // Check if right hand arg is a node, or an array of nodes. + // + // If it's an array of nodes. + // We assume that all are leafs, no nead to walk on them. + if n.Func != tsl.ArrayOp { + // It's a node. + n.Right, err = Walk(n.Right.(tsl.Node), checkColumnName) + if err != nil { + return n, err + } + } + } + + return n, nil + } +} diff --git a/v5/pkg/walkers/mongo/walk.go b/v5/pkg/walkers/mongo/walk.go new file mode 100644 index 0000000..ce84147 --- /dev/null +++ b/v5/pkg/walkers/mongo/walk.go @@ -0,0 +1,146 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package mongo helps to create mongo BSON filters using the TSL package. +package mongo + +import ( + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" +) + +// Returns the identifier setring from an IdentOp operator node. +func identString(n interface{}) string { + // This is an identifier. + if str, ok := n.(tsl.Node).Left.(string); ok { + return str + } + + // This is not an identifier. + return "" +} + +// bsonFromArray helper method creates a slice of bson values from an interface, +// supported values can be strings or floats. +func bsonFromArray(a interface{}) (values []interface{}, err error) { + n := a.(tsl.Node) + + // Check that this is an array node. + if n.Func != tsl.ArrayOp { + err = tsl.UnexpectedLiteralError{Literal: n.Func} + return + } + + nodes := n.Right.([]tsl.Node) + for _, v := range nodes { + // Check node value type. + switch l := v.Left.(type) { + case string, float64: + // Node value is string or float. + values = append(values, l) + default: + // Not a string or a float, + // We do not support values other then strings or floats. + err = tsl.UnexpectedLiteralError{Literal: v.Left} + return + } + } + + // If here this is a valid array. + return +} + +// Walk travel the TSL tree to create mongo-go-driver bson select operators. +// +// Users can call the Walk method to filter a mongo Find. +// +// // Prepare filter +// filter, _ := mongo.Walk(tree) +// +// // Run query +// cur, _ := collection.Find(ctx, filter) +// +// mongo-go-driver: https://go.mongodb.org/mongo-driver +// +func Walk(n tsl.Node) (b bson.D, err error) { + var values []interface{} + var l, r bson.D + + // Note: tsl function constants should be the same as mongo bson + // functions (e.g. tsl.AndOp is "$and" in tsl and in mongo bson), + // this is the reason we can use n.Func as a function name in the mongo + // bson Element construction. + switch n.Func { + case tsl.OrOp, tsl.AndOp: + l, err = Walk(n.Left.(tsl.Node)) + if err != nil { + return + } + r, err = Walk(n.Right.(tsl.Node)) + if err != nil { + return + } + b = bson.D{{n.Func, bson.A{l, r}}} + case tsl.EqOp, tsl.NotEqOp, tsl.LtOp, tsl.LteOp, tsl.GtOp, tsl.GteOp: + b = bson.D{{identString(n.Left), bson.D{{n.Func, n.Right.(tsl.Node).Left}}}} + case tsl.InOp, tsl.NotInOp: + values, err = bsonFromArray(n.Right) + if err != nil { + return + } + b = bson.D{{identString(n.Left), bson.D{{n.Func, values}}}} + case tsl.IsNilOp: + b = bson.D{{identString(n.Left), bson.D{{"$exists", false}}}} + case tsl.IsNotNilOp: + b = bson.D{{identString(n.Left), bson.D{{"$exists", true}}}} + case tsl.RegexOp: + b = bson.D{{identString(n.Left), primitive.Regex{n.Right.(tsl.Node).Left.(string), ""}}} + case tsl.NotRegexOp: + b = bson.D{{identString(n.Left), + bson.D{{"$not", primitive.Regex{n.Right.(tsl.Node).Left.(string), ""}}}}} + case tsl.BetweenOp: + // Mongo does not have a between function, translating sql's between into + // two none eq operators. + // Note: sql's between operator is inclusive: begin and end values are included. + values, err = bsonFromArray(n.Right) + if err != nil { + return + } + b = bson.D{{"$and", + bson.A{ + bson.D{{identString(n.Left), bson.D{{"$gte", values[0]}}}}, + bson.D{{identString(n.Left), bson.D{{"$lte", values[1]}}}}, + }}} + case tsl.NotBetweenOp: + // Mongo does not have a between function, translating sql's not between into + // two none eq operators. + values, err = bsonFromArray(n.Right) + if err != nil { + return + } + b = bson.D{{"$or", + bson.A{ + bson.D{{identString(n.Left), bson.D{{"$lt", values[0]}}}}, + bson.D{{identString(n.Left), bson.D{{"$gt", values[1]}}}}, + }}} + default: + // If here than the operator is not supported. + err = tsl.UnexpectedLiteralError{Literal: n.Func} + } + + return +} diff --git a/v5/pkg/walkers/semantics/walk.go b/v5/pkg/walkers/semantics/walk.go new file mode 100644 index 0000000..982b577 --- /dev/null +++ b/v5/pkg/walkers/semantics/walk.go @@ -0,0 +1,380 @@ +// Copyright 2019 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: 2019 Nimrod Shneor +// Author: 2019 Yaacov Zamir + +// Package semantics implements TSL tree semantics. +package semantics + +import ( + "fmt" + "regexp" + "strings" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" +) + +// EvalFunc is a key evaluation function type. +// +// The function gets a key (string), and returns the value (interface{}) stored in the data document for that key. +type EvalFunc = func(string) (interface{}, bool) + +// Walk travel the TSL tree and implements search semantics. +// +// Users can call the Walk method to check if a document compiles to `true` or `false` +// when applied to a tsl tree. +// +// Example: +// record := map[string]string { +// "title": "A good book", +// "author": "Joe", +// "spec.pages": 14, +// "spec.rating": 5, +// } +// +// // evalFactory creates an evaluation function for a data record. +// func evalFactory(r map[string]string) semantics.EvalFunc { +// // Returns: +// // A function (semantics.EvalFunc) that gets a `key` for a record and returns +// // the value of the document for that key. +// // If no value can be found for this `key` in our record, it will return +// // ok = false, if value is found it will return ok = true. +// return func(k string) (interface{}, bool) { +// v, ok := r[k] +// return v, ok +// } +// } +// +// // Check if our record complie with our tsl tree. +// // +// // For example: +// // if our tsl tree represents the tsl phrase "author = 'Joe'" +// // we will get the boolean value `true` for our record. +// // +// // if our tsl tree represents the tsl phrase "spec.pages > 50" +// // we will get the boolean value `false` for our record. +// eval := evalFactory(record) +// compliance, err = semantics.Walk(tree, eval) +// +func Walk(n tsl.Node, eval EvalFunc) (bool, error) { + l := n.Left.(tsl.Node) + + // Check for identifiers. + if l.Func == tsl.IdentOp { + newNode, err := handleIdent(n, eval) + if err != nil { + return false, err + } + return Walk(newNode, eval) + } + + // Implement tree semantics. + switch n.Func { + case tsl.EqOp, tsl.NotEqOp, tsl.LtOp, tsl.LteOp, tsl.GtOp, tsl.GteOp, tsl.RegexOp, tsl.NotRegexOp, + tsl.BetweenOp, tsl.NotBetweenOp, tsl.NotInOp, tsl.InOp, tsl.LikeOp, tsl.ILikeOp: + r := n.Right.(tsl.Node) + + switch l.Func { + case tsl.StringOp: + if r.Func == tsl.StringOp { + return handleStringOp(n, eval) + } + if r.Func == tsl.ArrayOp { + return handleStringArrayOp(n, eval) + } + case tsl.NumberOp: + if r.Func == tsl.NumberOp { + return handleNumberOp(n, eval) + } + if r.Func == tsl.ArrayOp { + return handleNumberArrayOp(n, eval) + } + case tsl.NullOp: + // Any comparison operation on a null element is false. + return false, nil + } + + return false, tsl.UnexpectedLiteralError{Literal: fmt.Sprintf("%v", r.Left)} + case tsl.IsNotNilOp: + return l.Func != tsl.NullOp, nil + case tsl.IsNilOp: + return l.Func == tsl.NullOp, nil + case tsl.AndOp, tsl.OrOp: + return handleLogicalOp(n, eval) + case tsl.NotOp: + return handleNotOp(n, eval) + } + + return false, tsl.UnexpectedLiteralError{Literal: n.Func} +} + +func handleIdent(n tsl.Node, eval EvalFunc) (tsl.Node, error) { + l := n.Left.(tsl.Node) + + _v, _ := eval(l.Left.(string)) + switch v := _v.(type) { + case string: + n.Left = tsl.Node{ + Func: tsl.StringOp, + Left: v, + } + case nil: + n.Left = tsl.Node{ + Func: tsl.NullOp, + Left: nil, + } + case bool: + val := "false" + if v { + val = "true" + } + n.Left = tsl.Node{ + Func: tsl.StringOp, + Left: val, + } + case float32: + n.Left = tsl.Node{ + Func: tsl.NumberOp, + Left: float64(v), + } + case float64: + n.Left = tsl.Node{ + Func: tsl.NumberOp, + Left: v, + } + case int32: + n.Left = tsl.Node{ + Func: tsl.NumberOp, + Left: float64(v), + } + case int64: + n.Left = tsl.Node{ + Func: tsl.NumberOp, + Left: float64(v), + } + case uint32: + n.Left = tsl.Node{ + Func: tsl.NumberOp, + Left: float64(v), + } + case uint64: + n.Left = tsl.Node{ + Func: tsl.NumberOp, + Left: float64(v), + } + case int: + n.Left = tsl.Node{ + Func: tsl.NumberOp, + Left: float64(v), + } + case uint: + n.Left = tsl.Node{ + Func: tsl.NumberOp, + Left: float64(v), + } + default: + return n, tsl.UnexpectedLiteralError{Literal: fmt.Sprintf("%s[%v]", l.Left.(string), v)} + } + + return n, nil +} + +func handleStringOp(n tsl.Node, eval EvalFunc) (bool, error) { + l := n.Left.(tsl.Node) + r := n.Right.(tsl.Node) + + left := l.Left.(string) + right := r.Left.(string) + + switch n.Func { + case tsl.EqOp: + return left == right, nil + case tsl.NotEqOp: + return left != right, nil + case tsl.LtOp: + return left < right, nil + case tsl.LteOp: + return left <= right, nil + case tsl.GtOp: + return left > right, nil + case tsl.GteOp: + return left >= right, nil + case tsl.LikeOp: + valid, err := regexp.Compile(likeToRegExp(right)) + if err != nil { + return false, tsl.UnexpectedLiteralError{Literal: right} + } + return valid.MatchString(left), nil + case tsl.ILikeOp: + valid, err := regexp.Compile(iLikeToRegExp(right)) + if err != nil { + return false, tsl.UnexpectedLiteralError{Literal: right} + } + return valid.MatchString(left), nil + case tsl.RegexOp: + valid, err := regexp.Compile(right) + if err != nil { + return false, tsl.UnexpectedLiteralError{Literal: right} + } + return valid.MatchString(left), nil + case tsl.NotRegexOp: + valid, err := regexp.Compile(right) + if err != nil { + return false, tsl.UnexpectedLiteralError{Literal: right} + } + return !valid.MatchString(left), nil + } + + return false, tsl.UnexpectedLiteralError{Literal: n.Func} +} + +func handleNumberOp(n tsl.Node, eval EvalFunc) (bool, error) { + l := n.Left.(tsl.Node) + r := n.Right.(tsl.Node) + + left := l.Left.(float64) + right := r.Left.(float64) + + switch n.Func { + case tsl.EqOp: + return left == right, nil + case tsl.NotEqOp: + return left != right, nil + case tsl.LtOp: + return left < right, nil + case tsl.LteOp: + return left <= right, nil + case tsl.GtOp: + return left > right, nil + case tsl.GteOp: + return left >= right, nil + } + + return false, tsl.UnexpectedLiteralError{Literal: n.Func} +} + +func handleStringArrayOp(n tsl.Node, eval EvalFunc) (bool, error) { + l := n.Left.(tsl.Node) + r := n.Right.(tsl.Node) + + left := l.Left.(string) + right := r.Right.([]tsl.Node) + + switch n.Func { + case tsl.BetweenOp: + begin := right[0].Left.(string) + end := right[1].Left.(string) + return left >= begin && left < end, nil + case tsl.NotBetweenOp: + begin := right[0].Left.(string) + end := right[1].Left.(string) + return left < begin || left >= end, nil + case tsl.InOp: + b := false + for _, node := range right { + b = b || left == node.Left.(string) + } + return b, nil + case tsl.NotInOp: + b := true + for _, node := range right { + b = b && left != node.Left.(string) + } + return b, nil + } + + return false, tsl.UnexpectedLiteralError{Literal: n.Func} +} + +func handleNumberArrayOp(n tsl.Node, eval EvalFunc) (bool, error) { + l := n.Left.(tsl.Node) + r := n.Right.(tsl.Node) + + left := l.Left.(float64) + right := r.Right.([]tsl.Node) + + switch n.Func { + case tsl.BetweenOp: + begin := right[0].Left.(float64) + end := right[1].Left.(float64) + return left >= begin && left < end, nil + case tsl.NotBetweenOp: + begin := right[0].Left.(float64) + end := right[1].Left.(float64) + return left < begin || left >= end, nil + case tsl.InOp: + b := false + for _, node := range right { + b = b || left == node.Left.(float64) + } + return b, nil + case tsl.NotInOp: + b := true + for _, node := range right { + b = b && left != node.Left.(float64) + } + return b, nil + } + + return false, tsl.UnexpectedLiteralError{Literal: n.Func} +} + +func handleLogicalOp(n tsl.Node, eval EvalFunc) (bool, error) { + l := n.Left.(tsl.Node) + r := n.Right.(tsl.Node) + + right, err := Walk(r, eval) + if err != nil { + return false, err + } + left, err := Walk(l, eval) + if err != nil { + return false, err + } + + switch n.Func { + case tsl.AndOp: + return right && left, nil + case tsl.OrOp: + return right || left, nil + } + + return false, tsl.UnexpectedLiteralError{Literal: n.Func} +} + +func handleNotOp(n tsl.Node, eval EvalFunc) (bool, error) { + l := n.Left.(tsl.Node) + + left, err := Walk(l, eval) + if err != nil { + return false, err + } + + return !left, nil +} + +func likeToRegExp(l string) string { + e := regexp.QuoteMeta(l) + e = strings.Replace(e, "%", ".*", -1) + e = strings.Replace(e, "_", ".", -1) + e = fmt.Sprintf("^%s$", e) + + return e +} + +func iLikeToRegExp(l string) string { + return "(?i)" + likeToRegExp(l) +} diff --git a/v5/pkg/walkers/semantics/walk_test.go b/v5/pkg/walkers/semantics/walk_test.go new file mode 100644 index 0000000..58dfaef --- /dev/null +++ b/v5/pkg/walkers/semantics/walk_test.go @@ -0,0 +1,70 @@ +// Copyright 2019 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package semantics + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" +) + +func TestWalk(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Walk") +} + +var _ = Describe("Walk", func() { + // This is the record that we will use for all the tests: + record := map[string]interface{}{ + "title": "A good book", + "author": "Joe", + "spec.pages": 14, + "spec.rating": 5, + } + + // This is the evaluation function that we will use to extract fields from the record: + eval := func(name string) (value interface{}, ok bool) { + value, ok = record[name] + return + } + + DescribeTable("Returns the expected result", + func(text string, expected bool) { + // Parse the text: + tree, err := tsl.ParseTSL(text) + Expect(err).ToNot(HaveOccurred()) + + // Get the value: + actual, err := Walk(tree, eval) + Expect(err).ToNot(HaveOccurred()) + Expect(actual).To(Equal(expected)) + }, + + // NOT combined with LIKE and ILIKE: + Entry("like good", "title like '%good%'", true), + Entry("like bad", "title like '%bad%'", false), + Entry("ilike GOOD", "title ilike '%GOOD%'", true), + Entry("ilike BAD", "title ilike '%BAD%'", false), + Entry("not like good", "title not like '%good%'", false), + Entry("not like bad", "title not like '%bad%'", true), + Entry("not ilike GOOD", "title not ilike '%GOOD%'", false), + Entry("not ilike BAD", "title not ilike '%BAD%'", true), + ) +}) diff --git a/v5/pkg/walkers/sql/doc_test.go b/v5/pkg/walkers/sql/doc_test.go new file mode 100644 index 0000000..3767b95 --- /dev/null +++ b/v5/pkg/walkers/sql/doc_test.go @@ -0,0 +1,49 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sql + +import ( + "fmt" + + sq "github.com/Masterminds/squirrel" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" +) + +// Example for the tsl package. +func Example() { + // Set a TSL input string. + input := "name = 'joe' and city != 'rome'" + + // Parse input string into a TSL tree. + tree, _ := tsl.ParseTSL(input) + + // Set filter + filter, _ := Walk(tree) + + // Convert TSL tree into SQL string using squirrel sql builder. + sql, args, _ := sq.Select("name, city, state"). + From("users"). + Where(filter). + ToSql() + + fmt.Printf("SQL : %s\n", sql) + fmt.Printf("Args: %v\n", args) + + // Output: + // SQL : SELECT name, city, state FROM users WHERE (name = ? AND city <> ?) + // Args: [joe rome] +} diff --git a/v5/pkg/walkers/sql/ops.go b/v5/pkg/walkers/sql/ops.go new file mode 100644 index 0000000..e98695d --- /dev/null +++ b/v5/pkg/walkers/sql/ops.go @@ -0,0 +1,113 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sql + +import ( + "fmt" + + sq "github.com/Masterminds/squirrel" + "github.com/yaacov/tree-search-language/v5/pkg/tsl" +) + +// Currently squirrel does not have a some needed operator expression. + +// notExpr handles SQL not. +type notExpr []sq.Sqlizer + +//nolint +func (n notExpr) ToSql() (sql string, args []interface{}, err error) { + if len(n) != 1 { + return "", nil, tsl.UnexpectedLiteralError{} + } + + partSQL, partArgs, err := n[0].ToSql() + if err != nil { + return "", nil, err + } + + args = append(args, partArgs...) + sql = fmt.Sprintf("NOT (%s)", partSQL) + + return +} + +// mathExpToSQL take a math expresion and return it's string, args and error. +func mathExpToSQL(n []sq.Sqlizer, mathOp string) (sql string, args []interface{}, err error) { + var left string + var right string + var partArgs []interface{} + + if len(n) != 2 { + return "", nil, tsl.UnexpectedLiteralError{} + } + + left, partArgs, err = n[0].ToSql() + if err != nil { + return "", nil, err + } + + args = append(args, partArgs...) + + right, partArgs, err = n[1].ToSql() + if err != nil { + return "", nil, err + } + + args = append(args, partArgs...) + sql = fmt.Sprintf("(%s %s %s)", left, mathOp, right) + + return +} + +// addExpr hanlhandlesdels SQL add. +type addExpr []sq.Sqlizer + +//nolint +func (n addExpr) ToSql() (sql string, args []interface{}, err error) { + return mathExpToSQL(n, "+") +} + +// subExpr hanlhandlesdels SQL subtract. +type subExpr []sq.Sqlizer + +//nolint +func (n subExpr) ToSql() (sql string, args []interface{}, err error) { + return mathExpToSQL(n, "-") +} + +// mulExpr hanlhandlesdels SQL multiply. +type mulExpr []sq.Sqlizer + +//nolint +func (n mulExpr) ToSql() (sql string, args []interface{}, err error) { + return mathExpToSQL(n, "*") +} + +// divExpr hanlhandlesdels SQL divide. +type divExpr []sq.Sqlizer + +//nolint +func (n divExpr) ToSql() (sql string, args []interface{}, err error) { + return mathExpToSQL(n, "/") +} + +// modExpr hanlhandlesdels SQL modulo. +type modExpr []sq.Sqlizer + +//nolint +func (n modExpr) ToSql() (sql string, args []interface{}, err error) { + return mathExpToSQL(n, "%") +} diff --git a/v5/pkg/walkers/sql/walk.go b/v5/pkg/walkers/sql/walk.go new file mode 100644 index 0000000..30512aa --- /dev/null +++ b/v5/pkg/walkers/sql/walk.go @@ -0,0 +1,194 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package sql helps to create SQL squirrel filters using the TSL package. +package sql + +import ( + "fmt" + "strconv" + + sq "github.com/Masterminds/squirrel" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" +) + +func nodesToStrings(in interface{}) (s []interface{}) { + var nn []tsl.Node + + // Check for nil + if in == nil { + return + } + + // Assume in is a node. + inNode := in.(tsl.Node) + + // Check for array node type. + if inNode.Func == tsl.ArrayOp { + // Take all nodes array from array RSE. + nn = inNode.Right.([]tsl.Node) + } else { + // Make a node array out of this node. + nn = []tsl.Node{inNode} + } + + // Assume all Nodes are Leafs. + for _, n := range nn { + s = append(s, n.Left) + } + + return +} + +// binaryStep handle a binary operator step for Walk. +func binaryStep(n tsl.Node) (s sq.Sqlizer, err error) { + var l, r sq.Sqlizer + + // Get left hand side node. + l, err = Walk(n.Left.(tsl.Node)) + if err != nil { + return + } + + // Get right hand side node. + r, err = Walk(n.Right.(tsl.Node)) + if err != nil { + return + } + + switch n.Func { + case tsl.AndOp: + s = sq.And{l, r} + case tsl.OrOp: + s = sq.Or{l, r} + case tsl.AddOp: + s = addExpr{l, r} + case tsl.SubtractOp: + s = subExpr{l, r} + case tsl.MultiplyOp: + s = mulExpr{l, r} + case tsl.DivideOp: + s = divExpr{l, r} + case tsl.ModuloOp: + s = modExpr{l, r} + default: + // If here than the operator is not supported. + err = tsl.UnexpectedLiteralError{Literal: n.Func} + } + + return +} + +// unaryStep handle a unary operator step for Walk. +func unaryStep(n tsl.Node) (s sq.Sqlizer, err error) { + var l sq.Sqlizer + var sql string + + l, err = Walk(n.Left.(tsl.Node)) + if err != nil { + return + } + + sql, _, err = l.ToSql() + if err != nil { + return + } + + right := nodesToStrings(n.Right) + + switch n.Func { + case tsl.NotOp: + s = notExpr{l} + case tsl.EqOp: + s = sq.Eq{sql: right[0]} + case tsl.NotEqOp: + s = sq.NotEq{sql: right[0]} + case tsl.LtOp: + s = sq.Lt{sql: right[0]} + case tsl.LteOp: + s = sq.LtOrEq{sql: right[0]} + case tsl.GtOp: + s = sq.Gt{sql: right[0]} + case tsl.GteOp: + s = sq.GtOrEq{sql: right[0]} + case tsl.InOp: + // Multiple eq will be translated into IN (?, ? ...). + s = sq.Eq{sql: right} + case tsl.NotInOp: + // Multiple not eq will be translated into NOT IN (?, ? ...). + s = sq.NotEq{sql: right} + case tsl.IsNilOp: + // eq nil will be translated into IS NULL. + s = sq.Eq{sql: nil} + case tsl.IsNotNilOp: + // not eq nil will be translated into IS NOT NULL. + s = sq.NotEq{sql: nil} + case tsl.LikeOp: + t := fmt.Sprintf("%s LIKE ?", sql) + s = sq.Expr(t, right[0]) + case tsl.ILikeOp: + t := fmt.Sprintf("%s ILIKE ?", sql) + s = sq.Expr(t, right[0]) + case tsl.BetweenOp: + t := fmt.Sprintf("%s BETWEEN ? AND ?", sql) + s = sq.Expr(t, right[0], right[1]) + case tsl.NotBetweenOp: + t := fmt.Sprintf("%s NOT BETWEEN ? AND ?", sql) + s = sq.Expr(t, right[0], right[1]) + default: + // If here than the operator is not supported. + err = tsl.UnexpectedLiteralError{Literal: n.Func} + } + + return +} + +// Walk travel the TSL tree to create squirrel SQL select operators. +// +// Users can call the Walk method inside a squirrel Where to add the query. +// +// filter, _ := sql.Walk(tree) +// sql, args, _ := sq.Select("name, city, state"). +// From("users"). +// Where(filter). +// ToSql() +// +// Squirrel: https://github.com/Masterminds/squirrel +// +func Walk(n tsl.Node) (s sq.Sqlizer, err error) { + switch n.Func { + case tsl.IdentOp: + s = sq.Expr(n.Left.(string)) + case tsl.NumberOp: + f := strconv.FormatFloat(n.Left.(float64), 'g', -1, 64) + s = sq.Expr(f) + case tsl.StringOp: + s = sq.Expr(n.Left.(string)) + case tsl.AndOp, tsl.OrOp, tsl.AddOp, tsl.SubtractOp, tsl.MultiplyOp, tsl.DivideOp, + tsl.ModuloOp: + return binaryStep(n) + case tsl.NotOp, tsl.EqOp, tsl.NotEqOp, tsl.LtOp, tsl.LteOp, tsl.GtOp, tsl.GteOp, + tsl.InOp, tsl.NotInOp, tsl.IsNilOp, tsl.IsNotNilOp: + return unaryStep(n) + case tsl.LikeOp, tsl.ILikeOp, tsl.BetweenOp, tsl.NotBetweenOp: + return unaryStep(n) + default: + // If here than the operator is not supported. + err = tsl.UnexpectedLiteralError{Literal: n.Func} + } + + return +} diff --git a/v5/pkg/walkers/sql/walk_test.go b/v5/pkg/walkers/sql/walk_test.go new file mode 100644 index 0000000..cee1f27 --- /dev/null +++ b/v5/pkg/walkers/sql/walk_test.go @@ -0,0 +1,65 @@ +// Copyright 2018 Yaacov Zamir +// and other contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sql + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" + + sq "github.com/Masterminds/squirrel" + + "github.com/yaacov/tree-search-language/v5/pkg/tsl" +) + +func TestSQLWalker(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "SQL walker") +} + +var _ = Describe("Walk", func() { + DescribeTable("Generates the expected SQL and arguments", + func(input string, expectedSQL string, expectedArgs ...interface{}) { + // Parse the input: + tree, err := tsl.ParseTSL(input) + Expect(err).ToNot(HaveOccurred()) + + // Convert the tree to a SQL filter: + filter, err := Walk(tree) + Expect(err).ToNot(HaveOccurred()) + + // Convert the filter to SQL text: + actualSQL, actualArgs, err := sq.Select("name, city, state"). + From("users"). + Where(filter). + ToSql() + Expect(err).ToNot(HaveOccurred()) + + // Check that the generated SQL and the arguments are the expected ones: + Expect(actualSQL).To(Equal(expectedSQL)) + Expect(actualArgs).To(Equal(expectedArgs)) + }, + + Entry( + "Search by name and city", + "name = 'joe' and city != 'rome'", + "SELECT name, city, state FROM users WHERE (name = ? AND city <> ?)", + "joe", "rome", + ), + ) +})