{"id":13850325,"url":"https://github.com/shivamMg/rd","last_synced_at":"2025-07-12T21:33:45.959Z","repository":{"id":57487579,"uuid":"125343738","full_name":"shivamMg/rd","owner":"shivamMg","description":"Build recursive descent parsers ","archived":false,"fork":false,"pushed_at":"2023-03-13T06:36:53.000Z","size":120,"stargazers_count":104,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-28T18:16:34.107Z","etag":null,"topics":["go","golang","grammar","recursive-descent-parser"],"latest_commit_sha":null,"homepage":"https://godoc.org/github.com/shivamMg/rd","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shivamMg.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-03-15T09:32:17.000Z","updated_at":"2024-11-25T10:36:09.000Z","dependencies_parsed_at":"2024-06-18T21:52:48.138Z","dependency_job_id":null,"html_url":"https://github.com/shivamMg/rd","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/shivamMg/rd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shivamMg%2Frd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shivamMg%2Frd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shivamMg%2Frd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shivamMg%2Frd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shivamMg","download_url":"https://codeload.github.com/shivamMg/rd/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shivamMg%2Frd/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265059650,"owners_count":23705222,"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":["go","golang","grammar","recursive-descent-parser"],"created_at":"2024-08-04T20:01:06.171Z","updated_at":"2025-07-12T21:33:45.683Z","avatar_url":"https://github.com/shivamMg.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# rd [![godoc](https://godoc.org/github.com/shivammg/rd?status.svg)](https://godoc.org/github.com/shivamMg/rd) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n`rd` is a small library to build hand-written recursive descent parsers. Besides exposing convenient methods to parse tokens it features automatic parse tree generation and flow tracing for debugging.\n\nRecursive descent parsers can imitate their grammar quite well. For instance, for the following grammar:\n\n```\nA → aB\nB → b\n```\n\n(where `A` and `B` are non-terminals, and `a` and `b` are terminals), a recursive descent parser might look like:\n\n```go\nfunc A() bool {\n    if nextToken() == \"a\" {\n        return B()\n    }\n    pushback(\"a\")\n    return false\n}\n\nfunc B() bool {\n    if nextToken() == \"b\" {\n        return true\n    }\n    pushback(\"b\")\n    return false\n}\n```\n\nParser for the same grammar written using `rd`:\n\n```go\nimport \"github.com/shivamMg/rd\"\n\nfunc A(b *rd.Builder) (ok bool) {\n    b.Enter(\"A\")\n    defer b.Exit(\u0026ok)\n\n    return b.Match(\"a\") \u0026\u0026 B(b)\n}\n\nfunc B(b *rd.Builder) (ok bool) {\n    defer b.Enter(\"B\").Exit(\u0026ok)\n\n    return b.Match(\"b\")\n}\n```\n\nA builder object keeps track of the current token and exposes convenient methods to write a parser. `Match` method, for instance, matches a token to the current token, and resets the original state in case of an unsuccessful match; there's no need for a manual `pushback`. As non-terminal functions are called, terminal matches, and calls to other non-terminal functions are done. A parse tree is generated in the builder object using these matches and calls:\n\nIn case of a successful match the terminal is added to parse tree under the current non-terminal (the one in which `Match` was called). Same goes in case of a non-terminal function call: the non-terminal, if it exits successfully, is added to parse tree under the current non-terminal. You can imagine this process being repeated recursively.\n\nArgument to `Enter` is what is added to parse tree as a symbol for the non-terminal. Argument to `Exit` determines if function call was successful or not.\n\n`ParseTree` method returns the parse tree which is, well, a tree data structure. It can be pretty-printed.\n\n```go\ntokens := []rd.Token{\"a\", \"b\"}\nb := rd.NewBuilder(tokens)\nif ok := A(b); ok {\n    fmt.Print(b.ParseTree())\n}\n```\n\nThe above snippet will print:\n\n```\nA\n├─ a\n└─ B\n   └─ b\n```\n\nA debug tree is also maintained which, unlike the parse tree, contains all matches and calls (not just the successful ones). It's helpful if you want to debug a parsing failure. It can be retrieved using the `DebugTree` method.\n\n```go\nfmt.Print(b.DebugTree())\n```\n\nThe above snippet will print:\n\n```\nA(true)\n├─ a = a\n└─ B(true)\n   └─ b = b\n```\n\nParsing errors can be retrieved using `Err` method. For `tokens := []rd.Token{\"a\", \"c\"}` the following statements:\n\n```go\nfmt.Println(b.Err())\nfmt.Print(b.DebugTree())\n```\n\nwill print:\n\n```\nparsing error\nA(false)\n├─ a = a\n└─ B(false)\n   └─ c ≠ b\n```\n\n\n## Examples\n\n### [Arithmetic expression parser](examples/arithmetic)\n\n```bash\ngo get github.com/shivamMg/rd/examples/arithmetic   # requires go modules support (go1.11+)\narithmetic -expr='3.14*4*(6/3)'  # hopefully $GOPATH/bin is in $PATH\narithmetic -expr='3.14*4*(6/3)' -backtrackingparser\n```\n\nParser and grammar for it can be found inside `examples/arithmetic/parser`. There's another parser written for a different grammar that also parses arithmetic expressions. This parser can be found inside `examples/arithmetic/backtrackingparser`. It uses backtracking - notice the use of `b.Backtrack()`.\n\nThis example lexer built using [chroma](https://github.com/alecthomas/chroma).\n\n\n### [PL/0 programming language parser](examples/pl0)\n\n```\ngo get github.com/shivamMg/rd/examples/pl0\ncd examples/pl0/\npl0 square.pl0\npl0 multiply.pl0\npl0 prime.pl0\n```\n\nParser and grammar can be found inside `examples/pl0/parser`. Grammar has been taken from [en.wikipedia.org/wiki/PL/0#Grammar](https://en.wikipedia.org/wiki/PL/0#Grammar). It also uses lexer built using chroma.\n\n### [Domain name parser](examples/domainname)\n\n```\ngo get github.com/shivamMg/rd/examples/domainname\ndomainname www.google.co.uk\n```\n\nGrammar has been taken from [www.ietf.org/rfc/rfc1035.txt](https://www.ietf.org/rfc/rfc1035.txt). The lexer is hand-written.\n\n## Licence\n\nMIT\n\n## Contribute\n\nContribute through bug fixes, improvements, new examples, etc. Lucky PR submitters get to walk home with a brand new CRT and an Audi 1987.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FshivamMg%2Frd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FshivamMg%2Frd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FshivamMg%2Frd/lists"}