{"id":16443395,"url":"https://github.com/0exp/jaina","last_synced_at":"2025-03-23T08:32:04.173Z","repository":{"id":56878306,"uuid":"192374602","full_name":"0exp/jaina","owner":"0exp","description":"Simple programming language builder inspired by interpreter pattern. You can build your own languages with custom operands and operators for any project purposes.","archived":false,"fork":false,"pushed_at":"2024-11-13T22:47:41.000Z","size":59,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-24T16:30:14.280Z","etag":null,"topics":["abstract-syntax-tree","interpreter-pattern","language-builder","ruby-interpreter"],"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/0exp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"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":"2019-06-17T15:40:09.000Z","updated_at":"2024-11-13T22:47:45.000Z","dependencies_parsed_at":"2024-10-11T09:31:13.043Z","dependency_job_id":null,"html_url":"https://github.com/0exp/jaina","commit_stats":{"total_commits":56,"total_committers":2,"mean_commits":28.0,"dds":0.0357142857142857,"last_synced_commit":"d0caa3aab617172b15918f18c5fb78a00cf29247"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fjaina","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fjaina/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fjaina/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fjaina/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0exp","download_url":"https://codeload.github.com/0exp/jaina/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245078067,"owners_count":20557274,"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":["abstract-syntax-tree","interpreter-pattern","language-builder","ruby-interpreter"],"created_at":"2024-10-11T09:20:20.435Z","updated_at":"2025-03-23T08:32:04.159Z","avatar_url":"https://github.com/0exp.png","language":"Ruby","readme":"# Jaina  \u0026middot; [![Gem Version](https://badge.fury.io/rb/jaina.svg)](https://badge.fury.io/rb/jaina)\n\nSimple programming language builder inspired by interpreter pattern.\nYou can build your own languages with custom operands and operators for any project purposes.\n\n## Installation\n\n```ruby\ngem 'jaina'\n```\n\n```shell\n$ bundle install\n# --- or ---\n$ gem install 'jaina'\n```\n\n```ruby\nrequire 'jaina'\n```\n\n## Usage\n\n- [Registered operators](#registered-operators)\n- [Register your own operator](#register-your-own-operator)\n- [Register your own operand](#register-your-own-operand)\n- [Context API](#context-api)\n- [Parse your code (build AST)](#parse-your-code-build-ast)\n- [Evaluate your code](#evaluate-your-code)\n- [Custom operator/operand arguments](#custom-operatoroperand-arguments)\n- [List registered operands and operators](#list-and-fetch-registered-operands-and-operators)\n- [Full example](#full-example)\n\n---\n\n### Registered operators\n\n- `AND`\n- `OR`\n- `NOT`\n- `(`, `)` (grouping operators)\n\n---\n\n### Register your own operator\n\n```ruby\n# step 1: define new operator\nclass But \u003c Jaina::NonTerminalExpr\n  token 'BUT' # use it in your program :)\n  associativity_direction :left # associativity (left or right)\n  acts_as_binary_term # binar or unary\n  precedence_level 4 # for example: AND \u003e OR, NOT \u003e AND, and etc...\nend\n\n# step 2: regsiter your operator\nJaina.register_expression(But)\n```\n\n---\n\n### Register your own operand\n\n```ruby\n# step 1: define new operand\nclass A \u003c Jaina::TerminalExpr\n  token 'A'\n\n  # NOTE: context is a custom data holder that passed from expression to expression\n  def evaluate(context)\n    # your custom evaluation code\n  end\nend\n\n# step 2: regsiter your operand\nJaina.register_expression(A)\n\n# step X: redefine existing operand (with the same token)\nclass NewA \u003c Jaina::TerminalExpr\n  token 'A'\nend\nJaina.redefine_expression(NewA)\n```\n\n---\n\n### Context API\n\n```ruby\nclass A \u003c Jaina::TerminalExpr\n  # ... some code\n\n  def evaluate(context)\n    ... your code ...\n    # ... context ???\n    ... your code ...\n  end\nend\n\n# NOTE: context api\n\ncontext.keys # =\u003e []\ncontext.set(:a, 1) # =\u003e 1\ncontext.get(:a) # =\u003e 1\ncontext.keys # =\u003e [:a]\ncontext.get(:b) # =\u003e Jaina::Parser::AST::Contex::UndefinedContextKeyError\n```\n\n---\n\n### Parse your code (build AST)\n\n```ruby\n# NOTE: without arguments\nJaina.parse('A AND B AND (C OR D) OR A AND (C OR E)')\n# =\u003e #\u003cJaina::Parser::AST:0x00007fd6f424a2e8\u003e\n\n# NOTE: with arguments\nJaina.parse('A[1,2] AND B[3,4]')\n# =\u003e #\u003cJaina::Parser::AST:0x00007fd6f424a2e9\u003e\n```\n\n---\n\n### Evaluate your code\n\n```ruby\nast = Jaina.parse('A AND B[5,test] AND (C OR D) OR A AND (C OR E)')\nast.evaluate\n\n# --- or ---\nJaina.evaluate('A AND B[5,test] AND (C OR D) OR A AND (C OR E)')\n\n# --- you can set initial context of your program ---\nJaina.evaluate('A AND B[5,test]', login: 'admin', logged_in: true)\n```\n\n---\n\n### Custom operator/operand arguments\n\n```ruby\n# NOTE: use []\nJaina.parse('A[1,true] AND B[false,\"false\"]')\n\n# NOTE:\n#   all your arguments will be typecasted to\n#   the concrete type inferred from the argument literal\n\nJaina.parse('A[1,true,false,\"false\"]') # 1, true, false \"false\"\n\n# NOTE: access to the argument list\nclass A \u003c Jaina::TerminalExpr\n  token 'A'\n\n  def evaluate(context)\n    # A[1,true,false,\"false\"]\n\n    arguments[0] # =\u003e 1\n    arguments[1] # =\u003e true\n    arguments[2] # =\u003e false\n    arguments[3] # =\u003e \"false\"\n  end\nend\n```\n\n---\n\n### List and fetch registered operands and operators\n\n```ruby\nA = Class.new(Jaina::TerminalExpr) { token 'A' }\nB = Class.new(Jaina::TerminalExpr) { token 'B' }\nC = Class.new(Jaina::TerminalExpr) { token 'C' }\n\nJaina.register_expression(A)\nJaina.register_expression(B)\nJaina.register_expression(C)\n\nJaina.expressions\n# =\u003e [\"AND\", \"OR\", \"NOT\", \"(\", \")\", \"A\", \"B\", \"C\"]\n\nJaina.fetch_expression(\"AND\") # =\u003e Jaina::Parser::Expression::Unit::And\nJaina.fetch_expression(\"A\") # =\u003e A\n\nJaina.fetch_expression(\"KEK\")\n# =\u003e raises Jaina::Parser::Expression::Registry::UnregisteredExpressionError\n```\n\n---\n\n### Full example\n\n```ruby\n# step 1: create new operand\nclass AddNumber \u003c Jaina::TerminalExpr\n  token 'ADD'\n\n  def evaluate(context)\n    context.set(:current_value, context.get(:current_value) + 10)\n  end\nend\n\n# step 2: create another new operand\nclass CheckNumber \u003c Jaina::TerminalExpr\n  token 'CHECK'\n\n  def evaluate(context)\n    context.get(:current_value) \u003c 0\n  end\nend\n\n# step 4: and another new :)\nclass InitState \u003c Jaina::TerminalExpr\n  token 'INIT'\n\n  def evaluate(context)\n    initial_value = arguments[0] || 0\n\n    context.set(:current_value, initial_value)\n  end\nend\n\n# step 5: register new oeprands\nJaina.register_expression(AddNumber)\nJaina.register_expression(CheckNumber)\nJaina.register_expression(InitState)\n\n# step 6: run your program\n\n# NOTE: with initial context\nJaina.evaluate('CHECK AND ADD', current_value: -1) # =\u003e 9\nJaina.evaluate('CHECK AND ADD', current_value: 2) # =\u003e false\n\n# NOTE: without initial context\nJaina.evaluate('INIT AND ADD') # =\u003e 10\nJaina.evaluate('INIT AND (CHECK OR ADD)') # =\u003e 10\n\n# NOTE: with arguments\nJaina.evaluate('INIT[100] AND ADD') =\u003e # 112\n```\n\n---\n\n## Contributing\n\n- Fork it ( https://github.com/0exp/jaina/fork )\n- Create your feature branch (`git checkout -b feature/my-new-feature`)\n- Commit your changes (`git commit -am 'Add some feature'`)\n- Push to the branch (`git push origin feature/my-new-feature`)\n- Create new Pull Request\n\n## License\n\nReleased under MIT License.\n\n## Authors\n\n[Rustam Ibragimov](https://github.com/0exp)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0exp%2Fjaina","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0exp%2Fjaina","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0exp%2Fjaina/lists"}