{"id":13798008,"url":"https://github.com/mbj/unparser","last_synced_at":"2025-04-10T15:43:06.671Z","repository":{"id":690054,"uuid":"9293459","full_name":"mbj/unparser","owner":"mbj","description":"Turn Ruby AST into semantically equivalent Ruby source","archived":false,"fork":false,"pushed_at":"2025-03-23T01:17:24.000Z","size":1284,"stargazers_count":313,"open_issues_count":6,"forks_count":47,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-04-09T20:01:47.426Z","etag":null,"topics":["parser","ruby","ruby-syntax","unparser"],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/mbj.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","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},"funding":{"github":"mbj"}},"created_at":"2013-04-08T09:54:20.000Z","updated_at":"2025-03-23T01:17:26.000Z","dependencies_parsed_at":"2024-05-12T03:22:23.758Z","dependency_job_id":"60a4471f-7117-4d1f-93d0-3b1f2fa1caae","html_url":"https://github.com/mbj/unparser","commit_stats":{"total_commits":941,"total_committers":30,"mean_commits":"31.366666666666667","dds":"0.12008501594048882","last_synced_commit":"f5c44b7225ba33421d6e1a5f6848fd75e4454f8b"},"previous_names":[],"tags_count":66,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbj%2Funparser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbj%2Funparser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbj%2Funparser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbj%2Funparser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mbj","download_url":"https://codeload.github.com/mbj/unparser/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248243497,"owners_count":21071054,"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":["parser","ruby","ruby-syntax","unparser"],"created_at":"2024-08-04T00:00:37.778Z","updated_at":"2025-04-10T15:43:06.660Z","avatar_url":"https://github.com/mbj.png","language":"Ruby","readme":"unparser\n========\n\n![CI](https://github.com/mbj/unparser/workflows/CI/badge.svg)\n[![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser)\n\nGenerate equivalent source for ASTs from [parser](https://github.com/whitequark/parser).\n\nThe following constraints apply:\n\n* No support for macruby extensions\n* Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format\n* Only support for Ruby \u003e= 3.2\n\nNotable Users:\n\n* [mutant](https://github.com/mbj/mutant) - Code review engine via mutation testing.\n* [ruby-next](https://github.com/ruby-next/ruby-next) - Ruby Syntax Backports.\n* Many other [reverse-dependencies](https://rubygems.org/gems/unparser/reverse_dependencies).\n\n(if you want your tool to be mentioned here please PR the addition with a TLDR of your use case).\n\nPublic API:\n-----------\n\nWhile unparser is in the `0.x` versions its public API can change any moment.\nI recommend to use `~\u003e 0.x.y` style version constraints that should give the best mileage.\n\nUsage\n-----\n\n```ruby\nrequire 'parser/current'\nrequire 'unparser'\n\nast = Unparser.parse('your(ruby(code))')\n\nUnparser.unparse(ast) # =\u003e 'your(ruby(code))'\n```\n\nTo preserve the comments from the source:\n\n```ruby\nrequire 'parser/current'\nrequire 'unparser'\n\nast, comments = Unparser.parse_with_comments('your(ruby(code)) # with comments')\n\nUnparser.unparse(ast, comments) # =\u003e 'your(ruby(code)) # with comments'\n```\n\nPassing in manually constructed AST:\n```ruby\nrequire 'parser/current'\nrequire 'unparser'\n\nmodule YourHelper\n  def s(type, *children)\n    Parser::AST::Node.new(type, children)\n  end\nend\n\ninclude YourHelper\n\nnode = s(:def,\n  :foo,\n  s(:args,\n    s(:arg, :x)\n  ),\n  s(:send,\n    s(:lvar, :x),\n    :+,\n    s(:int, 3)\n  )\n)\n\nUnparser.unparse(node) # =\u003e \"def foo(x)\\n  x + 3\\nend\"\n```\n\nNote: DO NOT attempt to pass in nodes generated via `AST::Sexp#s`, these ones return\nAPI incompatible `AST::Node` instances, unparser needs `Parser::AST::Node` instances.\n\nEquivalent vs identical:\n\n```ruby\nrequire 'unparser'\n\nnode = Unparser.parse(\u003c\u003c~'RUBY')\n  %w[foo bar]\nRUBY\n\ngenerated = Unparser.unparse(node) # [\"foo\", \"bar\"], NOT %w[foo bar] !\n\ncode == generated                 # false, not identical code\nUnparser.parse(generated) == node # true, but identical AST\n```\n\nSummary: unparser does not reproduce your source! It produces equivalent source.\n\nRuby Versions:\n--------------\n\nUnparsers primay reason for existance is mutant and its\nsupported [Ruby-Versions](https://github.com/mbj/mutant#ruby-versions).\n\nBasically: All non EOL MRI releases.\n\nIf you need to generate Ruby Syntax outside of this band feel free to contact me (email in gemspec).\n\nTesting:\n--------\n\nUnparser currently successfully round trips almost all ruby code around. Using Ruby \u003e= 2.6.\nIf there is a non round trippable example that is NOT subjected to known [Limitations](#limitations).\nplease report a bug.\n\nOn CI unparser is currently tested against rubyspec with minor [excludes](https://github.com/mbj/unparser/blob/master/spec/integrations.yml).\n\nLimitations:\n------------\n\nSource parsed with magic encoding headers other than UTF-8 and that have literal strings.\nwhere parts can be represented in UTF-8 will fail to get reproduced.\n\nA fix is possible as with latest updates the parser gem carries the information.\n\nExample:\n\nOriginal-Source:\n```ruby\n# -*- encoding: binary -*-\n\n\"\\x98\\x76\\xAB\\xCD\\x45\\x32\\xEF\\x01\\x01\\x23\\x45\\x67\\x89\\xAB\\xCD\\xEF\"\n```\n\nOriginal-AST:\n```\n(str \"\\x98v\\xAB\\xCDE2\\xEF\\x01\\x01#Eg\\x89\\xAB\\xCD\\xEF\")\n```\n\nGenerated-Source:\n\n```ruby\n\"\\x98v\\xAB\\xCDE2\\xEF\\x01\\x01#Eg\\x89\\xAB\\xCD\\xEF\"\n```\n\nGenerated-AST:\n\n```\n(str \"\\x98v\\xAB\\xCDE2\\xEF\\u0001\\u0001#Eg\\x89\\xAB\\xCD\\xEF\")\n```\n\nDiff:\n\n```\n@@ -1,2 +1,2 @@\n-(str \"\\x98v\\xAB\\xCDE2\\xEF\\x01\\x01#Eg\\x89\\xAB\\xCD\\xEF\")\n+(str \"\\x98v\\xAB\\xCDE2\\xEF\\u0001\\u0001#Eg\\x89\\xAB\\xCD\\xEF\")\n```\n\nInstallation\n------------\n\nInstall the gem `unparser` via your prefered method.\n\nPeople\n------\n\nVarious people contributed to this repository. See [Contributors](https://github.com/mbj/unparser/graphs/contributors).\n\nIncluded Libraries\n------------------\n\nFor dependency reduction reasons unparser ships vendored (and reduced) versions of:\n\n* [abstract_type](https://github.com/mbj/concord) -\u003e Unparser::AbstractType\n* [adamantium](https://github.com/dkubb/adamantium) -\u003e Unparser::Adamantium\n* [anima](https://github.com/mbj/concord) -\u003e Unparser::Anima\n* [concord](https://github.com/mbj/concord) -\u003e Unparser::Concord\n* [memoizable](https://github.com/dkubb/memoizable) -\u003e Unparser::Adamantium\n* [mprelude](https://github.com/dkubb/memoizable) -\u003e Unparser::Either\n\nContributing\n-------------\n\n* Fork the project.\n* Make your feature addition or bug fix.\n* Add tests for it. This is important so I don't break it in a\n  future version unintentionally.\n* Commit, do not mess with version\n  (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)\n* Send me a pull request. Bonus points for topic branches.\n\nKnown Users\n-------------\n\n* [RailsRocket](https://www.railsrocket.app) - A no-code app builder that creates Rails apps\n\nLicense\n-------\n\nSee LICENSE file.\n","funding_links":["https://github.com/sponsors/mbj"],"categories":["Ruby","Gems"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbj%2Funparser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmbj%2Funparser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbj%2Funparser/lists"}