{"id":17681526,"url":"https://github.com/i3oris/topdown","last_synced_at":"2025-04-19T17:10:12.239Z","repository":{"id":45863513,"uuid":"495771944","full_name":"I3oris/topdown","owner":"I3oris","description":"Parser shard using top down operator precedence and Pratt Parsing.","archived":false,"fork":false,"pushed_at":"2025-01-20T17:22:00.000Z","size":307,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T10:43:16.030Z","etag":null,"topics":["crystal","parser","pratt-parsing"],"latest_commit_sha":null,"homepage":"","language":"Crystal","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/I3oris.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":"2022-05-24T10:20:22.000Z","updated_at":"2025-02-16T23:56:44.000Z","dependencies_parsed_at":"2024-06-10T11:13:08.815Z","dependency_job_id":"c78fe5ba-5131-439b-8c98-52799b547b21","html_url":"https://github.com/I3oris/topdown","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/I3oris%2Ftopdown","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/I3oris%2Ftopdown/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/I3oris%2Ftopdown/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/I3oris%2Ftopdown/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/I3oris","download_url":"https://codeload.github.com/I3oris/topdown/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249746041,"owners_count":21319581,"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":["crystal","parser","pratt-parsing"],"created_at":"2024-10-24T09:11:22.425Z","updated_at":"2025-04-19T17:10:12.223Z","avatar_url":"https://github.com/I3oris.png","language":"Crystal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TopDown\n\nTopDown is a [crystal](https://crystal-lang.org) shard for writing a simple or evolved parser.\n\nTopDown is at an early development stage.\n\nIt aims to keep itself simple and efficient thanks to [Top down operator precedence and Pratt Parsing](https://en.wikipedia.org/wiki/Operator-precedence_parser) in which it's inspired.\n\nTopDown supports both token and tokenless parsing, each having their pros and cons, but each can fit better whether the use case.\n\nOperator precedence can be handled rather naturally and doesn't involve complex machinery under the hood, simply recursive descent under top down parsing theory.\n\n\u003c!-- This lead TopDown to be very fast. (benchmark are not yet ready) --\u003e\n\nTopDown is designed to be extensible. Writing parsing rules is made in code directly, allowing to insert custom behaviour in each step of parsing, storing state for a state-based parser, or make more complex parsing operation.\n\nFinally, TopDown is lightweight and doesn't require any dependencies.\n\n## Overview\n\nWrite a simple parser:\n```crystal\nrequire \"topdown\"\n\nclass MyParser \u003c TopDown::Parser\n  root :expression\n\n  syntax :expression do\n    parse!(/\\d+/).to_i\n  end\nend\n\nsource = \"1\"\nputs MyParser.new(source).parse # =\u003e 1\n```\n\nAdd basic operators with precedence:\n```crystal\nclass MyParser \u003c TopDown::Parser\n  ...\n\n  syntax :expression do\n    union do\n      parse(/\\d+/).to_i\n      infix(30, :pow)\n      infix(20, :mul)\n      infix(20, :div)\n      infix(10, :add)\n      infix(10, :sub)\n    end\n  end\n\n  syntax :pow, \"**\" { left() ** parse!(:expression) }\n  syntax :mul, '*' { left() * parse!(:expression) }\n  syntax :div, '/' { left() / parse!(:expression) }\n  syntax :add, '+' { left() + parse!(:expression) }\n  syntax :sub, '-' { left() - parse!(:expression) }\nend\n\nsource = \"3*6+6*4\"\nputs MyParser.new(source).parse # =\u003e 42\n```\n\nSkip whitespaces and comments:\n```crystal\nclass MyParser \u003c TopDown::Parser\n  ...\n\n  skip do\n    parse(' ' | '\\n' | '\\t')\n    parse(\"//\") { repeat { parse(not('\\n')) } }\n    parse(\"/*\") do\n      repeat { parse(not(\"*/\")) }\n      parse!(\"*/\")\n    end\n  end\nend\n\nsource = \"3*6  + /* comment */ 6*4 // comment\"\nputs MyParser.new(source).parse # =\u003e 42\n```\n\nAdd more prefix syntax:\n```crystal\nclass MyParser \u003c TopDown::Parser\n  ...\n\n  syntax :expression do\n    union do\n      parse(:parenthesis)\n      parse(:positif, with_precedence: 40)\n      parse(:negatif, with_precedence: 40)\n      parse(/\\d+/).to_i\n      infix(30, :pow)\n      infix(20, :mul)\n      infix(20, :div)\n      infix(10, :add)\n      infix(10, :sub)\n    end\n  end\n\n  syntax :parenthesis, '(' do\n    exp = parse!(:expression)\n    parse!(')', error: \"Parenthesis '(' is not closed\", at: begin_location())\n    exp\n  end\n\n  syntax :positif, '+' { +parse!(:expression) }\n  syntax :negatif, '-' { -parse!(:expression) }\nend\n\nsource = \"(-9 - 42 / (2*+5)**2) / -3\"\nputs MyParser.new(source).parse # =\u003e 3.14\n```\n\n## Installation\n\n1. Add the dependency to your `shard.yml`:\n\n   ```yaml\n   dependencies:\n     topdown:\n       github: I3oris/topdown\n   ```\n\n2. Run `shards install`\n\n## Documentation\n\nRead the [API documentation](https://i3oris.github.io/topdown/).\n\n## Benchmark\n\n```\n# === Small JSON ===\n#            Crystal JSON 147.76k (  6.77µs) (±17.12%)  2.77kB/op        fastest\n#            TopDown JSON  48.91k ( 20.45µs) (± 5.74%)  5.84kB/op   3.02× slower\n# TopDown JSON with token  28.58k ( 34.98µs) (± 5.65%)  7.96kB/op   5.17× slower\n# === Big JSON ===\n#            Crystal JSON 877.55  (  1.14ms) (±15.84%)   228kB/op        fastest\n#            TopDown JSON 281.64  (  3.55ms) (± 8.67%)  0.97MB/op   3.12× slower\n# TopDown JSON with token 255.37  (  3.92ms) (±26.02%)  1.14MB/op   3.44× slower\n```\n\n\u003e See the [benchmark code](./benchmark/benchmark.cr).\n\n## Roadmap\n\n- [ ] Write docs (almost complete)\n- [ ] Write spec (almost complete)\n- [x] Write readme\n- [ ] Write examples (in progress)\n- [x] Write benchmarks\n- [ ] Improve error handling\n- [ ] Improve tokens\n- [x] Improve characters parsing\n- [ ] Improve unions\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/your-github-user/topdown/fork\u003e)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Contributors\n\n- [I3oris](https://github.com/your-github-user) - creator and maintainer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fi3oris%2Ftopdown","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fi3oris%2Ftopdown","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fi3oris%2Ftopdown/lists"}