{"id":28824119,"url":"https://github.com/objectionary/phino","last_synced_at":"2026-05-24T19:05:08.069Z","repository":{"id":291834738,"uuid":"977982682","full_name":"objectionary/phino","owner":"objectionary","description":"Command-Line Normalizer, Rewriter, and Dataizer of 𝜑-Calculus Expressions","archived":false,"fork":false,"pushed_at":"2025-06-12T22:03:16.000Z","size":333,"stargazers_count":8,"open_issues_count":10,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-12T22:37:01.133Z","etag":null,"topics":["code-analysis","command-line-tool","eolang","formalization","haskell","normalization","phi-calculus"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/objectionary.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSES/MIT.txt","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,"zenodo":null}},"created_at":"2025-05-05T09:34:09.000Z","updated_at":"2025-06-12T22:03:18.000Z","dependencies_parsed_at":"2025-06-12T22:36:59.020Z","dependency_job_id":"dc8d3976-49f1-46f0-9b47-24f5c61e3bf9","html_url":"https://github.com/objectionary/phino","commit_stats":null,"previous_names":["objectionary/phino"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/objectionary/phino","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/objectionary%2Fphino","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/objectionary%2Fphino/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/objectionary%2Fphino/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/objectionary%2Fphino/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/objectionary","download_url":"https://codeload.github.com/objectionary/phino/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/objectionary%2Fphino/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260513639,"owners_count":23020567,"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":["code-analysis","command-line-tool","eolang","formalization","haskell","normalization","phi-calculus"],"created_at":"2025-06-19T01:03:42.870Z","updated_at":"2026-05-24T19:05:08.061Z","avatar_url":"https://github.com/objectionary.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\nSPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com\nSPDX-License-Identifier: MIT\n--\u003e\n\n# Command-Line Manipulator of 𝜑-Calculus Expressions\n\n[![DevOps By Rultor.com](https://www.rultor.com/b/objectionary/phino)](https://www.rultor.com/p/objectionary/phino)\n\n[![`phino` on Hackage](https://img.shields.io/hackage/v/phino)](http://hackage.haskell.org/package/phino)\n[![cabal-linux](https://github.com/objectionary/phino/actions/workflows/cabal.yml/badge.svg)](https://github.com/objectionary/phino/actions/workflows/cabal.yml)\n[![stack-linux](https://github.com/objectionary/phino/actions/workflows/stack.yml/badge.svg)](https://github.com/objectionary/phino/actions/workflows/stack.yml)\n[![codecov](https://codecov.io/gh/objectionary/phino/branch/master/graph/badge.svg)](https://app.codecov.io/gh/objectionary/phino)\n[![Haddock](https://img.shields.io/badge/docs-Haddock-blue.svg)](https://objectionary.github.io/phino/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSES/MIT.txt)\n[![Hits-of-Code](https://hitsofcode.com/github/objectionary/phino?branch=master\u0026label=Hits-of-Code)](https://hitsofcode.com/github/objectionary/phino/view?branch=master\u0026label=Hits-of-Code)\n[![PDD status](https://www.0pdd.com/svg?name=objectionary/phino)](https://www.0pdd.com/p?name=objectionary/phino)\n\nThis is a command-line normalizer, rewriter, and dataizer\nof [𝜑-calculus](https://www.eolang.org) expressions.\n\nFirst, you write a simple [𝜑-calculus](https://www.eolang.org) program\nin the `hello.phi` file:\n\n```text\nΦ ↦ ⟦ φ ↦ ⟦ Δ ⤍ 68-65-6C-6C-6F ⟧, t ↦ ξ.k, k ↦ ⟦⟧ ⟧\n```\n\n## Installation\n\nThen you can install `phino` in two ways:\n\nInstall [Cabal][cabal] first and then:\n\n```bash\ncabal update\ncabal install --overwrite-policy=always phino-0.0.0.71\nphino --version\n```\n\nOr download binary from the internet using [curl](https://curl.se/) or\n[wget](https://en.wikipedia.org/wiki/Wget):\n\n```bash\nsudo curl -o /usr/local/bin/phino http://phino.objectionary.com/releases/macos-15/phino-latest\nsudo chmod +x /usr/local/bin/phino\nphino --version\n```\n\nDownload paths are:\n\n* Ubuntu 22.04: \u003chttp://phino.objectionary.com/releases/ubuntu-22.04/phino-latest\u003e\n* Ubuntu 24.04: \u003chttp://phino.objectionary.com/releases/ubuntu-24.04/phino-latest\u003e\n* MacOS (ARM): \u003chttp://phino.objectionary.com/releases/macos-15/phino-latest\u003e\n* MacOS (Intel): \u003chttp://phino.objectionary.com/releases/macos-14-large/phino-latest\u003e\n* Windows: \u003chttp://phino.objectionary.com/releases/windows-2022/phino-latest.exe\u003e\n\n## Build\n\nTo build `phino` from source, clone this repository:\n\n```bash\ngit clone git@github.com:objectionary/phino.git\ncd phino\n```\n\nThen, run the following command (ensure you have [Cabal][cabal] installed):\n\n```bash\ncabal build all\n```\n\nNext, run this command to install `phino` system-wide:\n\n```bash\nsudo cp \"$(cabal list-bin phino)\" /usr/local/bin/phino\n```\n\nVerify that `phino` is installed correctly:\n\n```bash\n$ phino --version\n0.0.0.0\n```\n\nYou can ensure scripts are run with a specific version of `phino` using\nthe `--pin` global option. It exits with an error when the version supplied\ndoesn't match the installed one:\n\n```bash\nphino --pin=0.0.0.67 dataize hello.phi\n```\n\n## Dataize\n\nThen, you dataize the program:\n\n```bash\n$ phino dataize hello.phi\n68-65-6C-6C-6F\n```\n\n## Rewrite\n\nYou can rewrite this expression with the help of [rules](#rule-structure)\ndefined in the `my-rule.yml` YAML file (here, the `!d` is a capturing group,\nsimilar to regular expressions):\n\n```yaml\nname: My custom rule\npattern: Δ ⤍ !d\nresult: Δ ⤍ 62-79-65\n```\n\nThen, rewrite:\n\n```bash\n$ phino rewrite --rule=my-rule.yml hello.phi\nΦ ↦ ⟦ φ ↦ ⟦ Δ ⤍ 62-79-65 ⟧, t ↦ ξ.k, k ↦ ⟦⟧ ⟧\n```\n\nIf you want to use many rules, just use `--rule` as many times as you need:\n\n```bash\nphino rewrite --rule=rule1.yaml --rule=rule2.yaml ...\n```\n\nYou can also use [built-in rules](resources), which are designed\nto normalize expressions:\n\n```bash\nphino rewrite --normalize hello.phi\n```\n\nIf no input file is provided, the 𝜑-expression is taken from `stdin`:\n\n```bash\n$ echo 'Φ ↦ ⟦ φ ↦ ⟦ Δ ⤍ 68-65-6C-6C-6F ⟧ ⟧' | phino rewrite --rule=my-rule.yml\nΦ ↦ ⟦ φ ↦ ⟦ Δ ⤍ 62-79-65 ⟧ ⟧\n```\n\nYou're able to pass [`XMIR`][xmir] as input. Use `--input=xmir` and `phino`\nwill parse given `XMIR` from file or `stdin` and convert it to `phi` AST.\n\n```bash\nphino rewrite --rule=my-rule.yaml --input=xmir file.xmir\n```\n\nAlso `phino` supports 𝜑-expressions in\n[ASCII](https://en.wikipedia.org/wiki/ASCII) format and with\nsyntax sugar. The `rewrite` command also allows you to desugar the expression\nand print it in canonical syntax:\n\n```bash\n$ echo 'Q -\u003e [[ @ -\u003e Q.io.stdout(\"hello\") ]]' | phino rewrite\nΦ ↦ ⟦\n  φ ↦ Φ.io.stdout(\n    α0 ↦ Φ.string(\n      α0 ↦ Φ.bytes(\n        α0 ↦ ⟦ Δ ⤍ 68-65-6C-6C-6F ⟧\n      )\n    )\n  )\n⟧\n```\n\n## Merge\n\nYou can merge several 𝜑-programs into a single one by merging their\ntop level formations:\n\n```bash\n$ cat bytes.phi\n{⟦ bytes(data) ↦ ⟦ φ ↦ data ⟧ ⟧}\n$ cat number.phi\n{⟦\n  number(as-bytes) ↦ ⟦\n    φ ↦ as-bytes,\n    plus(x) ↦ ⟦ λ ⤍ L_number_plus ⟧\n  ⟧\n⟧}\n$ cat minus.phi\n{⟦ number ↦ ⟦ minus(x) ↦ ⟦ λ ⤍ L_number_minus ⟧ ⟧ ⟧}\n$ phino merge bytes.phi number.phi minus.phi --sweet\n{⟦\n  bytes(data) ↦ ⟦ φ ↦ data ⟧,\n  number(as-bytes) ↦ ⟦\n    φ ↦ as-bytes,\n    plus(x) ↦ ⟦ λ ⤍ L_number_plus ⟧,\n    minus(x) ↦ ⟦ λ ⤍ L_number_minus ⟧\n  ⟧\n⟧}\n```\n\n## Match\n\nYou can test the 𝜑-program matches against the [rule](#rule-structure)\npattern. The result output contains matched substitutions:\n\n```bash\n$ phino match --pattern='⟦ Δ ⤍ !d, !B ⟧' hello.phi\nB \u003e\u003e ⟦ ρ ↦ ∅ ⟧\nd \u003e\u003e 68-65-6C-6C-6F\n```\n\n## Explain\n\nYou can _explain_ rewriting rule by printing them in [LaTeX][latex] format:\n\n```bash\n$ phino explain --normalize\n\\begin{tabular}{rl}\n\\trrule{alpha}\n  { [[ B_1, \\tau_1 -\u003e ?, B_2 ]] ( \\tau_2 -\u003e e ) }\n  { [[ B_1, \\tau_1 -\u003e ?, B_2 ]] ( \\tau_1 -\u003e e ) }\n  { if $ \\indexof{ \\tau_2 } = \\vert B_1 \\vert $ }\n  { }\n\\trrule{dc}\n  { T ( \\tau -\u003e e ) }\n  { T }\n  { }\n  { }\n...\n\\trrule{stop}\n  { [[ B ]] . \\tau }\n  { T }\n  { if $ \\tau \\notin B \\;\\text{and}\\; @ \\notin B \\;\\text{and}\\; L \\notin B $ }\n  { }\n\\end{tabular}\n```\n\nFor more details, use `phino [COMMAND] --help` option.\n\n## Rule structure\n\nThis is BNF-like yaml rule structure. Here types ended with\napostrophe, like `Attribute'` are built types from 𝜑-program [AST](src/AST.hs)\n\n```bnfc\nRule:\n  name: String\n  pattern: String\n  result: String\n  when: Condition?       # predicate, works with substitutions before extension\n  where: [Extension]?    # substitution extensions\n  having: Condition?     # predicate, works with substitutions after extension\n\nCondition:\n  = and: [Condition]     # logical AND\n  | or:  [Condition]     # logical OR\n  | not: Condition       # logical NOT\n  | alpha: Attribute'    # check if given attribute is alpha\n  | eq:                  # compare two comparable objects\n      - Comparable\n      - Comparable\n  | in:                  # check if attributes exist in bindings\n      - Attribute'\n      - Binding'\n  | nf: Expression'      # returns True if given expression in normal form\n                         # which means that no more other normalization rules\n                         # can be applied\n  | xi: Expression'      # special condition for Rcopy normalization rule to\n                         # avoid infinite recursion while the condition checking\n                         # returns True if there's no ξ outside of the formation\n                         # in given expression.\n  | matches:             # returns True if given expression after dataization\n      - String           # matches to given regex\n      - Expression\n  | part-of:             # returns True if given expression is attached to any\n      - Expression'      # attribute in ginve bindings\n      - BiMeta'\n\nComparable:              # comparable object that may be used in 'eq' condition\n  = Attribute'\n  | Number\n  | Expression'\n\nNumber:                  # comparable number\n  = Integer              # just regular integer\n  | index: Attribute'    # calculate index of alpha attribute\n  | length: BiMeta'      # calculate length of bindings by given meta binding\n\nExtension:               # substitutions extension used to introduce new meta variables\n  meta: [ExtArgument]    # new introduced meta variable\n  function: String       # name of the function\n  args: [ExtArgument]    # arguments of the function\n\nExtArgument\n  = Bytes'               # !d\n  | Binding'             # !B\n  | Expression'          # !e\n  | Attribute'           # !a\n```\n\nHere's list of functions that are supported for extensions:\n\n* `contextualize` - function of two arguments, that rewrites given expression\n  depending on provided context according to the contextualization\n  [rules](assets/contextualize.jpg)\n* `scope` - resolves the scope for given expression. Works only with meta\n  expressions denotes as `𝑒` or `!e`. The scope is nearest outer formation,\n  if it's present. In all other cases the default scope is used, which is\n  anonymous formation `⟦ ρ ↦ ∅ ⟧`.\n* `random-tau` - creates attribute with random unique name. Accepts bindings,\n  and attributes. Ensures that created attribute is not present in list of\n  provided attributes and does not exist as attribute in provided bindings.\n* `dataize` - dataizes given expression and returns bytes.\n* `concat` - accepts bytes or dataizable expressions as arguments,\n  concatenates them into single sequence and convert it to expression\n  that can be pretty printed as human readable string:\n  `Φ.string(Φ.bytes⟦ Δ ⤍ !d ⟧)`.\n* `sed` - pattern replacer, works like unix `sed` function.\n  Accepts two arguments: target expression and pattern.\n  Pattern must start with `s/`, consists of three parts\n  separated by `/`, for example, this pattern `s/\\\\s+//g`\n  replaces all the spaces with empty string. To escape braces and slashes\n  in pattern and replacement parts - use them with `\\\\`,\n  e.g. `s/\\\\(.+\\\\)//g`.\n* `random-string` - accepts dataizable expression or bytes as pattern.\n  Replaces `%x` and `%d` formatters with random hex numbers and\n  decimals accordingly. Uniqueness is guaranteed during one\n  execution of `phino`.\n* `size` - accepts exactly one meta binding and returns size of it and\n  `Φ.number`.\n* `tau` - accepts `Φ.string`, dataizes it and converts it to attribute.\n  If dataized string can't be converted to attribute - an error is thrown.\n* `string` - accepts `Φ.string` or `Φ.number` or attribute and converts it\n  to `Φ.string`.\n* `number` - accepts `Φ.string` and converts it `Φ.number`\n* `sum` - accepts list of `Φ.number` or `Φ.bytes` and returns sum of them as `Φ.number`\n* `join` - accepts list of bindings and returns list of joined bindings. Duplicated\n  `ρ`, `Δ` and `λ` attributes are ignored, all other duplicated attributes are replaced\n  with unique attributes using `random-tau` function.\n* `splice` - accepts three arguments: input bindings `𝐵-in`, a sentinel expression,\n  and replacement bindings `𝐵-rep`. Returns a new binding group where `𝐵-rep`\n  is inserted in front of every binding in `𝐵-in` whose value is a formation\n  with `φ` equal to the sentinel. Every spliced copy of `𝐵-rep` has its\n  `τ`-labelled attributes renamed via `random-tau` so the resulting binding\n  group has no duplicates. `𝐵-rep` must contain only `τ`-labelled bindings\n  (`BiTau (AtLabel _) _` or `BiVoid (AtLabel _)`); any `ρ`, `Δ`, `λ`, `φ`,\n  `α`, or meta-attribute binding in `𝐵-rep` makes the function fail fast\n  because such bindings cannot be renamed and would produce duplicates\n  when spliced at more than one position. When no binding matches the\n  sentinel, the output equals the input unchanged.\n* `graft` - same call shape as `splice` (`𝐵-in`, sentinel, `𝐵-rep`) and the\n  same renaming guarantees, but every matched sentinel binding is replaced by\n  the renamed copy of `𝐵-rep` instead of preserved in place. Use it when the\n  marker must not survive the substitution — for example, when fusing two\n  bodies that both produce the same effect and the original marker would fire\n  a second time. When no binding matches the sentinel, the output equals the\n  input unchanged.\n\n## Meta variables\n\nThe `phino` supports meta variables to write 𝜑-expression patterns for\ncapturing attributes, bindings, etc.\n\nThis is the list of supported meta variables:\n\n* `!a` || `𝜏` - attribute\n* `!e` || `𝑒` - any expression\n* `!B` || `𝐵` - list of bindings\n* `!d` || `δ` - bytes in meta delta binding\n* `!t` - tail after expression, sequence of applications and/or dispatches,\n         must start only with dispatch\n* `!F` - function name in meta lambda binding\n\nEvery meta variable may also be used with an integer index, like `!B1` or `𝜏0`.\n\nIncorrect usage of meta variables in 𝜑-expression patterns leads to\nparsing errors.\n\n## Benchmark\n\nTo run performance benchmarks, you need [Java 8+][java] and [curl][curl].\nMaven is downloaded automatically on first run via `benchmark/mvnw`.\n\nThe benchmark uses the compiled [`Native`][jna-native] class from\n[JNA][jna] — a large real-world Java class — as its test input.\nOn first run, `make bench` downloads the class, disassembles it to\n[XMIR][xmir] via [jeo-maven-plugin][jeo], converts it to 𝜑 using\n`phino rewrite`, and caches the results in `benchmark/tmp/`.\nSubsequent runs skip straight to the benchmarks.\n\n```bash\nmake bench\n```\n\n\u003c!-- benchmark_begin --\u003e\n\n```text\n=== parse/phi ===\n  warmup:     3 iterations\n  batches:    10 x 1\n  total:      1283128.483 μs\n  avg:        128312.848 μs\n  min:        117090.148 μs\n  max:        159904.869 μs\n  std dev:    16799.275 μs\n=== parse/xmir ===\n  warmup:     3 iterations\n  batches:    10 x 1\n  total:      7638674.160 μs\n  avg:        763867.416 μs\n  min:        696616.654 μs\n  max:        834131.587 μs\n  std dev:    46054.709 μs\n=== rewrite/normalize ===\n  warmup:     3 iterations\n  batches:    10 x 1\n  total:      410101.450 μs\n  avg:        41010.145 μs\n  min:        37322.375 μs\n  max:        46790.558 μs\n  std dev:    3315.786 μs\n=== print/sweet/multiline ===\n  warmup:     3 iterations\n  batches:    10 x 1\n  total:      4536347.235 μs\n  avg:        453634.724 μs\n  min:        436266.864 μs\n  max:        484874.805 μs\n  std dev:    14385.579 μs\n=== print/sweet/flat ===\n  warmup:     3 iterations\n  batches:    10 x 1\n  total:      4391468.284 μs\n  avg:        439146.828 μs\n  min:        421949.972 μs\n  max:        461548.132 μs\n  std dev:    12926.007 μs\n=== print/salty/multiline ===\n  warmup:     3 iterations\n  batches:    10 x 1\n  total:      13872760.194 μs\n  avg:        1387276.019 μs\n  min:        1347848.058 μs\n  max:        1435996.600 μs\n  std dev:    28902.131 μs\n```\n\nThe results were calculated in [this GHA job][benchmark-gha]\non 2026-05-24 at 14:39,\non Linux with 4 CPUs.\n\n\u003c!-- benchmark_end --\u003e\n\n## How to Contribute\n\nFork repository, make changes, then send us a [pull request][guidelines].\nWe will review your changes and apply them to the `master` branch shortly,\nprovided they don't violate our quality standards. To avoid frustration,\nbefore sending us your pull request please make sure all your tests pass:\n\n```bash\nmake all\n```\n\nTo generate a local coverage report for development, run:\n\n```bash\nmake coverage\n```\n\nYou will need [GHC ≥ 9.6.7][GHC] and [Cabal ≥ 3.0 (recommended)][cabal]\nor [Stack ≥ 3.0][stack] installed.\n\n[cabal]: https://www.haskell.org/cabal/\n[stack]: https://docs.haskellstack.org/en/stable/install_and_upgrade/\n[GHC]: https://www.haskell.org/ghc/\n[guidelines]: https://www.yegor256.com/2014/04/15/github-guidelines.html\n[xmir]: https://news.eolang.org/2022-11-25-xmir-guide.html\n[latex]: https://en.wikipedia.org/wiki/LaTeX\n[java]: https://www.java.com/en/download/\n[curl]: https://curl.se/\n[jna]: https://github.com/java-native-access/jna\n[jna-native]: https://github.com/java-native-access/jna/blob/master/src/com/sun/jna/Native.java\n[jeo]: https://github.com/objectionary/jeo-maven-plugin\n[benchmark-gha]: https://github.com/objectionary/phino/actions/runs/26364073830\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobjectionary%2Fphino","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobjectionary%2Fphino","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobjectionary%2Fphino/lists"}