{"id":23734044,"url":"https://github.com/woojiahao/chris","last_synced_at":"2025-09-03T07:37:38.699Z","repository":{"id":48149883,"uuid":"516026603","full_name":"woojiahao/chris","owner":"woojiahao","description":"Pratt parser implementation in Go","archived":false,"fork":false,"pushed_at":"2022-07-24T09:39:15.000Z","size":109,"stargazers_count":44,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-05T06:31:54.111Z","etag":null,"topics":["go","golang","lexer","lexer-parser","parser","pratt-parser"],"latest_commit_sha":null,"homepage":"https://woojiahao.notion.site/Chris-s-Pratt-Parsing-Notes-a3ccdbc32a424be6bcf67f52769ebd94","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/woojiahao.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}},"created_at":"2022-07-20T15:00:48.000Z","updated_at":"2024-11-13T21:56:40.000Z","dependencies_parsed_at":"2022-08-12T19:40:38.174Z","dependency_job_id":null,"html_url":"https://github.com/woojiahao/chris","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/woojiahao/chris","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woojiahao%2Fchris","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woojiahao%2Fchris/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woojiahao%2Fchris/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woojiahao%2Fchris/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/woojiahao","download_url":"https://codeload.github.com/woojiahao/chris/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woojiahao%2Fchris/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273409767,"owners_count":25100447,"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","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","lexer","lexer-parser","parser","pratt-parser"],"created_at":"2024-12-31T05:33:29.198Z","updated_at":"2025-09-03T07:37:38.673Z","avatar_url":"https://github.com/woojiahao.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# chris\n\nPratt parser implementation in Go for parsing mathematical equations\n\n---\n\nThe core implementation details follows the advice by\nBob Nystrom detailed in\nhis [article on Pratt parsing](http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/)\n\nMy notes on Pratt parsing and this project can be\nfound [here.](https://woojiahao.notion.site/Pratt-Parsing-Notes-a3ccdbc32a424be6bcf67f52769ebd94)\n\n`chris` hopes to allow for user input mathematical equations that can be parsed and compiled into valid Go functions\nthat can be used with plotting libraries in Go like `gonum/plot`. However, there are many other ways to use such a\nlibrary.\n\n## Sample\n\n`chris` supports most mathematical equations that Desmos supports. Additional operators will be added down the line. To\nview the current operators, refer [here.](#operatorssymbols)\n\n```text\n1 + 2 * 3               := 1 + (2 * 3)\nsin(pi/4)               := sin((pi/4))\n2^x + cos(pi/4 + 15)    := (2^x) + cos(((pi/4) + 15))\n```\n\n## Usage\n\nTo use `chris` in your own project, download it as a package in Go modules:\n\n```command\ngo get github.com/woojiahao/chris\n```\n\nTo set up a basic compiler, we will use both the `lexer` and `parser` modules. The `lexer` generates the token stream\nand the `parser` will be able to parse that token stream into a given Abstract Syntax Tree (AST). For more information\nabout the roles of either component, refer below.\n\n`lexer` receives a keyword and constant list to determine how these tokens are tokenized.\n\n`parser` only requires the `lexer` to generate the AST. To retrieve the AST, we simply call `parser#Parse`.\n\n```go\npackage compiler\n\nimport (\n\t\"fmt\"\n\t\"github.com/woojiahao/chris/pkg/lexer\"\n\t\"github.com/woojiahao/chris/pkg/parser\"\n)\n\ntype Compiler struct {\n\tl *lexer.Lexer\n\tp *parser.Parser\n}\n\nfunc New(exp string) *Compiler {\n\tkeywords := []string{\"sin\", \"cos\", \"tan\", \"csc\", \"sec\", \"cot\"}\n\tconstants := []string{\"pi\"}\n\tl := lexer.New(exp, keywords, constants)\n\tp := parser.New(l)\n\n\t// Parse expression and get AST. We ignore the err for now\n\tast, _ := p.Parse()\n\tfmt.Printf(\"AST: %v\\n\", ast)\n\n\treturn \u0026Compiler{l, p}\n}\n```\n\nRefer to `example/` for a sample compiler which parses the equation and generates a function of\ntype `func(float64) float64` that can be used in plotting libraries like `gonum/plot`.\n\n## Architecture\n\nThe general architecture of a programming language compiler can be found here:\n\n```mermaid\nflowchart LR\n    Lexer--\u003eParser--\u003eCompiler\n```\n\n1. `Lexer` - acts as an iterator over a given expression and converts each character/word into a given token. It ignores\n   whitespaces and will parse numbers and words as a whole chunk.\n\n2. `Parser` - reads the token stream from a given Lexer and applies grammar to the tokens to generate an AST tree. It is\n   not responsible for checking if the keywords are valid. It just needs to know that the expression can generate a\n   valid AST tree.\n\n3. `Compiler` - receives the generated AST tree from the Parser and performs operations on the given AST tree and the\n   respective nodes. `chris`, however is not a compiler, but a parser, so it will not compile the given AST.\n\n### Parselets\n\nParser logic is performed by something known as \"Parselets\". Effectively, they are the components that handles behavior\nof each token. This is slightly different to having functions per non-terminal character in our grammar.\n\nWe have two kinds of parselets, prefix and infix. Prefix parselets are what can start an independent sub-expression like\nnumbers, `(` or variables, while infix parselets require a left and right sub-expression to generate a node.\n\n## Operators/Symbols\n\n| Symbol      | Purpose                                                     | Position     | Precedence |\n|-------------|-------------------------------------------------------------|--------------|------------|\n| +           | Addition                                                    | Infix        | 2          |\n| -           | Subtraction                                                 | Prefix/Infix | 2          |\n| *           | Multiplication                                              | Infix        | 3          |\n| /           | Division                                                    | Infix        | 3          |\n| ^           | Exponent                                                    | Infix        | 4          |\n| (           | Create sub-expression or encapsulate a function's arguments | Prefix/Infix | 5          |\n| )           | End sub-expression                                          | -            | -1         |\n| =           | Assignment                                                  | Infix        | 1          |\n| \\\u003ckeyword\u003e  | Keyword that corresponds to a function                      | Infix        | 1          |\n| \\\u003cnumber\u003e   | Number                                                      | Prefix       | 1          |\n| \\\u003cvariable\u003e | Single character to represent a variable                    | Prefix       | 1          |\n| \\\u003cconstant\u003e | User-specified constant                                     | Prefix       | 1          |\n\n## BNF\n\n```elixir\n# chris BNF\n# General terminals\n\u003cdigit\u003e         ::= '0' | ... | '9'\n\u003cletter\u003e        ::= 'a' | ... | 'z'\n                | 'A' | ... | 'Z'\n\n# Terminals in chris\n\u003cnumber\u003e        ::= \u003cdigit\u003e \n                | \u003cdigit\u003e'.'\u003cdigit\u003e\n                | \u003cnumber\u003e\u003cdigit\u003e\n\u003cvariable\u003e      ::= \u003cletter\u003e\n\u003ckeyword\u003e       ::= \u003cletter\u003e+\n\n# Non-terminals\n\u003coperator\u003e      ::= '+' | '-' | '*' | '/' | '^'\n\u003cunary\u003e         ::= '-'\n\u003cexpression\u003e    ::= \u003cnumber\u003e\n                | \u003cvariable\u003e\n                | \u003ckeyword\u003e \n                | \u003cunary\u003e \u003cexpression\u003e \n                | \u003cexpression\u003e \u003coperator\u003e \u003cexpression\u003e \n                | \u003cexpression\u003e \u003cexpression\u003e \n                | \u003cfunction call\u003e \n                | \u003cgroup\u003e\n\u003cfunction call\u003e ::= \u003ckeyword\u003e '(' \u003cexpression\u003e* ')'\n\u003cgroup\u003e         ::= '(' \u003cexpression\u003e ')'\n\u003cassignment\u003e    ::= \u003cvariable\u003e '=' \u003cexpression\u003e\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwoojiahao%2Fchris","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwoojiahao%2Fchris","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwoojiahao%2Fchris/lists"}