{"id":16787169,"url":"https://github.com/yaacov/tree-search-language","last_synced_at":"2026-03-14T19:23:56.195Z","repository":{"id":49045724,"uuid":"145689805","full_name":"yaacov/tree-search-language","owner":"yaacov","description":"Tree Search Language (TSL) is a wonderful search langauge.","archived":false,"fork":false,"pushed_at":"2025-03-13T12:46:59.000Z","size":1120,"stargazers_count":61,"open_issues_count":0,"forks_count":11,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-02T04:08:24.758Z","etag":null,"topics":["antlr4-grammar","golnag","mongodb","query-language","query-parser","searching","sql"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yaacov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-22T09:53:05.000Z","updated_at":"2025-03-08T12:27:45.000Z","dependencies_parsed_at":"2025-03-08T12:19:00.950Z","dependency_job_id":"aad7cdcc-87b1-4005-b29f-f27b25e4e934","html_url":"https://github.com/yaacov/tree-search-language","commit_stats":null,"previous_names":["yaacov/tsl"],"tags_count":65,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaacov%2Ftree-search-language","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaacov%2Ftree-search-language/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaacov%2Ftree-search-language/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaacov%2Ftree-search-language/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yaacov","download_url":"https://codeload.github.com/yaacov/tree-search-language/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247994119,"owners_count":21030050,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["antlr4-grammar","golnag","mongodb","query-language","query-parser","searching","sql"],"created_at":"2024-10-13T08:14:23.752Z","updated_at":"2026-03-14T19:23:56.185Z","avatar_url":"https://github.com/yaacov.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/yaacov/tree-search-language/master/v6/img/search-162.png\" alt=\"TSL Logo\"\u003e\n\u003c/p\u003e\n\n# Tree Search Language (TSL)\n\nTree Search Language (TSL) is a wonderful human readable filtering language.\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/yaacov/tree-search-language/v6)](https://goreportcard.com/report/github.com/yaacov/tree-search-language/v6)\n[![GoDoc](https://img.shields.io/static/v1?label=godoc\u0026message=reference\u0026color=blue)](https://pkg.go.dev/github.com/yaacov/tree-search-language/v6/pkg/tsl?tab=doc)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\nThe TSL language grammar is human readable and similar to SQL syntax.\n\n## Awesome:\n\n-  [What I can do with it ?](#what-i-can-do-with-it-)\n-  [What does it do ?](#what-does-it-do-)\n-  [Cool logo](#cool-logo)\n-  [Install](#install)\n-  [Syntax examples](#syntax-examples)\n-  [Types](#types)\n-  [Code examples](#code-examples)\n-  [CLI tools](#cli-tools)\n-  [Grammar](#grammar)\n\n## What I can do with it ?\n\nYou 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.\n\n([more examples](/v6/cmd/))\n\n[kubestl-sql](https://github.com/yaacov/kubectl-sql) uses TSL to select Kubernetes resources based on the value of one or more resource fields.\n\nHere is our `tsl_mem`CLI tool ([code](/v6/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`:\n\n``` bash\n$  ./tsl_mem -i \"rating is not null and author ~= 'Joe'\" | jq\n```\n``` json\n[\n  {\n    \"author\": \"Joe\",\n    \"spec.pages\": 100,\n    \"spec.rating\": 4,\n    \"title\": \"Book\"\n  },\n  {\n    \"author\": \"Joe\",\n    \"spec.pages\": 150,\n    \"spec.rating\": 4,\n    \"title\": \"Good Book\"\n  },\n  {\n    \"author\": \"Joe\",\n    \"spec.pages\": 15,\n    \"spec.rating\": 5,\n    \"title\": \"My Big Book\"\n  }\n]\n```\n\n``` bash\n $  ./tsl_mem -i \"rating is null and pages \u003c 250\" | jq\n ```\n ``` json\n[\n  {\n    \"author\": \"Jane\",\n    \"spec.pages\": 50,\n    \"title\": \"Some Other Book\"\n  }\n]\n\n```\n\n## What does it do ?\n\nThe TSL package parses `tsl phrases` into `tsl trees`, it also include extra `walkers` that iterate (walk) over the `tsl tree` to perform exhilarating tasks, for example, convert a `tsl tree` into an SQL expression, create in-memory search engines, BSON object exporters and even more exciting stuff. \n\n#### Parsing tsl phrases\n\nFor example, this `tsl phrase`:\n\n``` sql\nname like '%joe%' and (city = 'paris' or city = 'milan')\n```\n\nWill be parsed into this `tsl tree`:\n![TSL](/v6/img/example_a.png?raw=true \"example tree\")\n\n## Cool logo\n\nAwesome logo image by [gophers...](https://github.com/egonelbre/gophers).\n\n## Install\n\n#### Building from source using go modules\n\n``` bash\n$ go version\ngo version go1.23.4 linux/amd64\n```\n\nClone the TSL `git` repository, and run `make`:\n\n``` bash\ngit clone git@github.com:yaacov/tree-search-language.git\ncd tree-search-language/v6\nmake\n```\n\nOther `make` options include `make lint` for linting check and `make test` for tests.\n\n#### Installing the different packages using `go get`\n\n``` bash\n# Install the base package\ngo get \"github.com/yaacov/tree-search-language/v6/pkg/tsl\"\n\n# Install all walkers\ngo get \"github.com/yaacov/tree-search-language/v6/pkg/walkers/...\"\n\n# Or pick the walker needed\ngo get \"github.com/yaacov/tree-search-language/v6/pkg/walkers/sql\"\ngo get \"github.com/yaacov/tree-search-language/v6/pkg/walkers/semantics\"\ngo get \"github.com/yaacov/tree-search-language/v6/pkg/walkers/ident\"\ngo get \"github.com/yaacov/tree-search-language/v6/pkg/walkers/graphviz\"\n```\n\n#### Installing the command line example using `go install`\n\nSee CLI tools usage [here](https://github.com/yaacov/tree-search-language#cli-tools).\n\n``` bash\ngo install -v \"github.com/yaacov/tree-search-language/v6/cmd/tsl_parser\"\n```\n\n## Syntax examples\n\n#### Operator precedence\n\nThis TSL phrase:\n\n``` sql\nname like '%joe%' and (city = 'paris' or city = 'milan')\n```\n\nWill be parsed into this TSL tree:\n![TSL](/v6/img/example_a.png?raw=true \"example tree\")\n\n#### Operators with multiple arguments\n\nThis TSL phrase:\n\n``` sql\nname in ('joe', 'jane') and grade not between 0 and 50\n```\n\nWill be parsed into this TSL tree:\n![TSL](/v6/img/example_b.png?raw=true \"example tree\")\n\n#### Math operators\n\nThis TSL phrase:\n\n``` sql\nmemory.total - memory.cache \u003e 2000 and cpu.usage \u003e 50\n```\n\nWill be parsed into this TSL tree:\n![TSL](/v6/img/example_c.png?raw=true \"example tree\")\n\n#### More math operators\n\nThis TSL phrase:\n\n``` sql\n(net.rx + net.tx) / 1000 \u003e 3 or net.rx / 1000 \u003e 6\n```\n\nWill be parsed into this TSL tree:\n\n![TSL](/v6/img/example_d.png?raw=true \"example tree\")\n\n#### SI units\n\nThis TSL phrase:\n\n``` sql\n(net.rx + net.tx) \u003c 1Ki\n```\n\nWill be parsed into this TSL tree:\n\n![TSL](/v6/img/example_e.png?raw=true \"example tree\")\n\nImages created using the `tsl_parser` CLI example and Graphviz's `dot` utility:\n``` bash\n$ ./tsl_parser -i \"name like '%joe%' and (city = 'paris' or city = 'milan')\" -o dot \u003e file.dot\ndot file.dot -Tpng \u003e image.png\n```\n\n## Types\n\n#### Booleans\n\n```  sql\nsupported = true\n```\n\nWill be parsed into this TSL tree:\n\n![TSL](/v6/img/example_g.png?raw=true \"example tree\")\n\n#### Dates (RFC3339)\n\n```  sql\ndate = 2020-01-01T20:00:00Z\n```\n\nWill be parsed into this TSL tree:\n\n![TSL](/v6/img/example_h.png?raw=true \"example tree\")\n\n## Code examples\n\nFor complete working code examples, see the CLI tools [directory](/v6/cmd)\n( see more on TSL's CLI tools usage [here](https://github.com/yaacov/tree-search-language#cli-tools) ).\n\n##### ParseTSL\n\nThe `tsl` package include the ParseTSL [code](/v6/pkg/tsl/tsl.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v6/pkg/tsl#ParseTSL) method for parsing TSL into a search tree:\n``` go\ntree, err := tsl.ParseTSL(\"name in ('joe', 'jane') and grade not between 0 and 50\")\nif err != nil {\n    log.Fatal(err)\n}\n```\n\nAfter parsing the TSL tree will look like this (image created using the `tsl_parser` cli utility using `.dot` output option):\n\n![TSL](/v6/img/example01.png?raw=true \"example tree\")\n\n##### sql.Walk\n\nThe `walkers` `sql` package include a helper sql.Walk ([code](/v6/pkg/walkers/sql/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v6/pkg/walkers/sql#Walk)) method that adds search to [squirrel](https://github.com/Masterminds/squirrel)'s SelectBuilder object:\n\n``` go\nimport (\n    ...\n    sq \"github.com/Masterminds/squirrel\"\n    \"github.com/yaacov/tree-search-language/v6/pkg/walkers/sql\"\n    ...\n)\n\n// Parse a TSL phrase into a TSL tree.\ntree, err := tsl.ParseTSL(\"name in ('joe', 'jane') and grade not between 0 and 50\")\nif err != nil {\n    log.Fatal(err)\n}\n\n// Prepare squirrel filter.\nfilter, err := sql.Walk(tree)\nif err != nil {\n    log.Fatal(err)\n}\n\n// Create an SQL query.\nsql, args, err := sq.Select(\"name\", \"city\", \"state\").\n    From(\"users\").\n    Where(filter).\n    ToSql()\nif err != nil {\n    log.Fatal(err)\n}\n```\n\nAfter SQL generation the `sql` and `args` vars will be:\n``` sql\nSELECT name, city, state FROM users WHERE (name IN (?,?) AND grade NOT BETWEEN ? AND ?)\n```\n\n``` json\n[\"joe\", \"jane\", 0, 50]\n\n```\n\n##### graphviz.Walk\n\nThe `walkers` `graphviz`  package include a helper graphviz.Walk ([code](/v6/pkg/walkers/graphviz/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v6/pkg/walkers/graphviz#Walk)) method that exports `.dot` file nodes :\n\n``` go\nimport (\n    ...\n    \"github.com/yaacov/tree-search-language/v6/pkg/walkers/graphviz\"\n    ...\n)\n\n// Parse a TSL phrase into a TSL tree.\ntree, err := tsl.ParseTSL(\"name in ('joe', 'jane') and grade not between 0 and 50\")\nif err != nil {\n    log.Fatal(err)\n}\n\n// Prepare .dot file nodes as a string.\ns, err = graphviz.Walk(\"\", tree, \"\")\nif err != nil {\n    log.Fatal(err)\n}\n\n// Wrap the nodes in a digraph wrapper.\ns = fmt.Sprintf(\"digraph {\\n%s\\n}\\n\", s)\n```\n\n##### ident.Walk\n\nThe `walkers` `ident`  package include a helper ident.Walk ([code](/v6/pkg/walkers/ident/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v6/pkg/walkers/ident#Walk)) method that checks and mapps identifier names:\n\n``` go\nimport (\n    ...\n    \"github.com/yaacov/tree-search-language/v6/pkg/walkers/ident\"\n    ...\n)\n...\n\n// columnNamesMap mapps between user namespace and the SQL column names.\nvar columnNamesMap = map[string]string{\n\t\"title\":       \"title\",\n\t\"author\":      \"author\",\n\t\"spec.pages\":  \"pages\",\n\t\"spec.rating\": \"rating\",\n}\n\n// checkColumnName checks if a column name is valid in user space replace it\n// with the mapped column name and returns and error if not a valid name.\nfunc checkColumnName(s string) (string, error) {\n\t// Check for column name in map.\n\tif v, ok := columnNamesMap[s]; ok {\n\t\treturn v, nil\n\t}\n\n\t// If not found return string as is, and an error.\n\treturn s, fmt.Errorf(\"column \\\"%s\\\" not found\", s)\n}\n...\n\n// Parse a TSL phrase into a TSL tree.\ntree, err := tsl.ParseTSL(\"name in ('joe', 'jane') and grade not between 0 and 50\")\nif err != nil {\n    log.Fatal(err)\n}\n\n// Check and replace user identifiers with the SQL table column names.\nnewTree, err = ident.Walk(tree, checkColumnName)\nif err != nil {\n    log.Fatal(err)\n}\n...\n```\n\n##### semantics.Walk\n\nThe `walkers` `semantics`  package include a helper semantics.Walk ([code](/v6/pkg/walkers/semantics/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v6/pkg/walkers/semantics#Walk)) method that helps filter a list of objects using a `tsl tree`, and a `type` `semantics.EvalFunc` ([code](/v6/pkg/walkers/semantics/walk.go), [doc](https://pkg.go.dev/github.com/yaacov/tree-search-language/v6/pkg/walkers/semantics#EvalFunc)) that return a record's value for a record key:\n\n``` go\nimport (\n    ...\n    \"github.com/yaacov/tree-search-language/v6/pkg/walkers/semantics\"\n    ...\n)\n...\n\n// evalFactory creates an evaluation function for a data record.\n//\n// Returns:\n// A function that gets a `key` for a record and returns the value.\n// If no value can be found for this `key` in our record, it will return\n// ok = false, if value is found it will return ok = true.\nfunc evalFactory(book Book) semantics.EvalFunc {\n\treturn func(k string) (interface{}, bool) {\n\t\tif v, ok := book[k]; ok {\n\t\t\treturn v, true\n\t\t}\n\t\treturn nil, false\n\t}\n}\n\n// Check if a record complies with our tsl tree.\nrecord := map[string]string{\n    \"title\":       \"A good book\",\n    \"author\":      \"Joe\",\n    \"spec.pages\":  14,\n    \"spec.rating\": 5.0,\n}\neval := evalFactory(record)\ncompliance, err := semantics.Walk(tree, eval)\nif err != nil {\n    log.Fatal(err)\n}\n```\n\n## CLI tools\n\nThe example CLI tools showcase the TSL language and `tsl` golang package, see the [cmd](/v6/cmd) directory for code.\n\n##### tsl_parser\n\n`tsl_parser` is a basic example, showing how to parse a `tsl phrase` into a `tsl tree`.\n\n``` bash\n$ ./tsl_parser -h\nUsage of ./tsl_parser:\n  -i string\n    \tthe tsl string to parse (e.g. \"animal = 'kitty'\")\n  -o string\n    \toutput format [json/yaml/sql/dot] (default \"json\")\n```\n\n\n``` bash\n$ ./tsl_parser -i \"(name = 'joe' or name = 'jane') and city = 'rome'\" -o sql\n```\n```\nsql:  SELECT * FROM table_name WHERE ((name = ? OR name = ?) AND city = ?)\nargs: [joe jane rome]\n```\n\n``` bash\n$ ./tsl_parser -i \"(name = 'joe' or name = 'jane') and city = 'rome'\" | jq\n```\n\n``` json\n{\n  \"type\": \"BINARY_EXP\",\n  \"operator\": \"AND\",\n  \"left\": {\n    \"type\": \"BINARY_EXP\",\n    \"operator\": \"OR\",\n    \"left\": {\n      \"type\": \"BINARY_EXP\",\n      \"operator\": \"EQ\",\n      \"left\": {\n        \"type\": \"IDENTIFIER\",\n        \"value\": \"name\"\n      },\n      \"right\": {\n        \"type\": \"STRING\",\n        \"value\": \"joe\"\n      }\n    },\n    \"right\": {\n      \"type\": \"BINARY_EXP\",\n      \"operator\": \"EQ\",\n      \"left\": {\n        \"type\": \"IDENTIFIER\",\n        \"value\": \"name\"\n      },\n      \"right\": {\n        \"type\": \"STRING\",\n        \"value\": \"jane\"\n      }\n    }\n  },\n  \"right\": {\n    \"type\": \"BINARY_EXP\",\n    \"operator\": \"EQ\",\n    \"left\": {\n      \"type\": \"IDENTIFIER\",\n      \"value\": \"city\"\n    },\n    \"right\": {\n      \"type\": \"STRING\",\n      \"value\": \"rome\"\n    }\n  }\n}\n```\n\n``` bash\n$ ./tsl_parser -i \"city = 'rome'\" -o dot\n```\n\n``` dot\ndigraph {\nroot [shape=box color=black label=\"$eq\"]\nXVlB [shape=record color=red label=\"$ident | 'city'\" ]\nzgba [shape=record color=blue label=\"$string | 'rome'\" ]\nroot -\u003e { XVlB, zgba }\n}\n```\n\nConvert the .dot file into a PNG image using Graphviz's dot utility:\n\n``` bash\n# Convert .dot file to PNG\n$ dot -Tpng file.dot -o tree.png\n```\n\n##### tsl_mem\n\n`tsl_mem` is an advanced example showing a custom walker, implementing in-memory sql server.\n\n``` bash\n $ ./tsl_mem -i \"rating \u003e 4 and title ~= 'Big'\" -o yaml\n ```\n ``` yaml\n- author: Joe\n  spec.pages: 15\n  spec.rating: 5\n  title: My Big Book\n```\n\n## Grammar\n\n##### Flex and Bison grammar\n\nTSL parser is generated using [Flex](https://github.com/westes/flex) and [Bison](https://www.gnu.org/software/bison/), the grammar files are:\n- [tsl_lexer.l](/v6/pkg/parser/tsl_lexer.l) - Lexical analyzer (Flex)\n- [tsl_parser.y](/v6/pkg/parser/tsl_parser.y) - Grammar parser (Bison)\n\n##### Keywords\n```\nand or not is null like ilike between in\n```\n##### Operators\n```\n= \u003c= \u003e= != ~= ~! \u003c\u003e + - * / %\n```\n\n##### Special Literals\n```\nDate (YYYY-MM-DD)\nRFC3339 datetime\nNumbers with SI unit suffixes (Ki, Mi, Gi, Ti, Pi or K, M, G, T, P)\nString literals (quoted with ', \" or `)\nIdentifiers (including dots)\nBoolean literals (true/false)\nNull\n```\n\n##### Identifiers\n\nIdentifiers in TSL can include letters, digits, underscores, dots, slashes, hyphens, and parentheses. They can also include array suffixes with indices, wildcards, or complex identifiers.\n\nExamples:\n- `name`\n- `pods[0].status`\n- `containers[2].ports[80].protocol`\n- `nodes[*].status`\n- `deployments[my-deployment.spec.containers/nginx].image`\n- `services[my.service].status`\n- `nodes[my/node].status`\n- `pods[my-pod]`\n- `func()`\n- `obj.func()`\n- `arr[0].func()`\n- `obj.arr[0].func()`\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyaacov%2Ftree-search-language","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyaacov%2Ftree-search-language","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyaacov%2Ftree-search-language/lists"}