{"id":20925853,"url":"https://github.com/comby-tools/comby-reducer","last_synced_at":"2025-05-13T17:33:16.698Z","repository":{"id":38745960,"uuid":"335527433","full_name":"comby-tools/comby-reducer","owner":"comby-tools","description":"A simple program reducer for any language.","archived":false,"fork":false,"pushed_at":"2023-05-11T05:04:48.000Z","size":1248,"stargazers_count":73,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-24T18:52:05.338Z","etag":null,"topics":["fuzzing","reducer","rewriting","transformation"],"latest_commit_sha":null,"homepage":"https://comby.dev/blog/2021/03/26/comby-reducer","language":"JavaScript","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/comby-tools.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2021-02-03T06:18:50.000Z","updated_at":"2024-12-05T11:28:59.000Z","dependencies_parsed_at":"2024-11-19T05:54:18.457Z","dependency_job_id":null,"html_url":"https://github.com/comby-tools/comby-reducer","commit_stats":{"total_commits":39,"total_committers":1,"mean_commits":39.0,"dds":0.0,"last_synced_commit":"f97bc1b9b503879767db525ce6841683bf00b63c"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/comby-tools%2Fcomby-reducer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/comby-tools%2Fcomby-reducer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/comby-tools%2Fcomby-reducer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/comby-tools%2Fcomby-reducer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/comby-tools","download_url":"https://codeload.github.com/comby-tools/comby-reducer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253993581,"owners_count":21996343,"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":["fuzzing","reducer","rewriting","transformation"],"created_at":"2024-11-18T20:35:26.469Z","updated_at":"2025-05-13T17:33:16.206Z","avatar_url":"https://github.com/comby-tools.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# comby-reducer\n\nA program and data format reducer for arbitrary language syntax. Produces\nhuman comprehensible output. Define declarative transformations with ease.\n\n![reduce-gif-900](https://user-images.githubusercontent.com/888624/112452187-9514de80-8d13-11eb-8d44-939f4ce2b582.gif)\n\n# Quick start\n\nInstall the `comby-reducer` binary on your path with\n[npm](https://www.npmjs.com/get-npm):\n\n```\nnpm install -g @comby-tools/comby-reducer\n```\n\nInvoke it like this:\n\n```\ncomby-reducer \u003cfile-to-reduce\u003e --transforms ./transforms -- \u003ccrashing-program\u003e @@\n```\n\nTo feed the file input to `\u003ccrasing-=program\u003e` via `stdin`, invoke it like this instead:\n\n```\ncomby-reducer \u003cfile-to-reduce\u003e --stdin --transforms ./transforms -- \u003ccrashing-program\u003e\n```\n\n## Alternative local install\n\nInstall `comby-reducer` in a local directory at\n`./node_modules/.bin/comby-reducer`. If you see some warnings just ignore them.\n\n```\nnpm install @comby-tools/comby-reducer\n```\n\n## Example\n\nLet's say you just ran a program that crashed a compiler and want to find a\nsmaller example program that triggers the same crash. We'll simulate how to\nfind a smaller example program with `comby-reducer`.\n\n**Step 1.** Clone the repository\n\n```\ngit clone https://github.com/comby-tools/comby-reducer\n```\n\nIn [`example/program.c`](./example/program.c) you'll find the program we'll reduce:\n\n```c\n#include\u003cstdio.h\u003e\n#include\u003cstring.h\u003e\n\nint main(int argc, char **argv) {\n  if (argv[1]) {\n      printf(\"I can't believe it's not butter\");\n  }\n  // But I want to believe it's not butter...\n  memset(NULL, 1, 1);\n}\n```\n\nThe `memset` statement causes a crash when we run this program. There's some\njunk in there that we don't need to trigger the crash. Let's get started.\n\n**Step 2.** Go into the `example` directory\n\n```\ncd example\n```\n\nNext, we'll use a \"pretend compiler\" that crashes when it \"compiles\" our\nprogram (in reality, our \"compiler\" crashes when it runs a valid C program, not\nwhen actually compiling it, but we'll suspend our greater knowledge for now).\n\n**Step 3**: Run this command to crash the compiler\n\n```bash\n./compiler.sh program.c\n```\n\nYou'll see something like this at the end: `./compiler.sh: line 7: 41936 Segmentation fault: 11  ./program`\n\n**Step 4**: Reduce the program\n\n```bash\ncomby-reducer program.c --file /tmp/in.c --lang .c --transforms ../transforms -- ./compiler.sh @@\n```\n\nYou should see:\n\n```\n[+] Loaded 22 transformation rules\n[+] Did pass 0 pass\n[+] Did pass 1 pass\n[+] Did pass 2 pass\n[+] Did pass 3 pass\n[+] Result:\n#include\u003cstring.h\u003e\nvoid main() {\n  memset(NULL, 1, 1);\n}\n```\n\nNice, our program is smaller! `comby-reduce` found  that a smaller valid\nprogram keeps crashing our \"compiler\", but without the cruft.\n\nLet's break down the command invocation:\n\n- The part after `--` is the command we want to run that causes a crash. In our case, `./compiler.sh @@`\n  - The `@@` part is substituted with a file containing a program (like `program.c`)\n  - To feed input from `stdin`, remove the `@@` and add the `--stdin` command line flag.\n\n- `--file /tmp/in.c` says that the `@@` we substitute should be named `/tmp/in.c`. The `.c` extension may matter if our compiler expects a file with a `.c` extesion, for example. `comby-reducer` will try borrow the extension of the original file but `--file` exists to give you control over the file name that your program sees.\n\n- `--lang .c` says that the language we want to reduce is C-like. `comby-reducer` uses language definitions to parse input according to some language. This matters so that our transforms can accurately match strictly code blocks and avoids bothering with not-actually-code-syntax that come up in comments and strings. This may not be a big deal. You can use `--lang .generic` if you have some DSL or smart contract language. Here's the list of [specific language parsers](https://comby.dev/docs/overview#does-it-work-on-my-language).\n\n- `--transforms \u003cdir\u003e` loads transform definitions from `.toml` files in the specified `dir` (default dir is `transforms`). Transforms are specified in a [TOML format](https://comby.dev/docs/configuration#toml-format) using [`comby` syntax](https://comby.dev/docs/syntax-reference). See [Usage](#Usage) below for more details.\n\n## Usage\n\n### Transformations\n\n`comby-reducer` makes it easy to write rules for transformation using [comby syntax](https://comby.dev/docs/syntax-reference).\nA handful of defaults are included in\n[`transforms/config.toml`](transforms/config.toml) that will probably get you\nvery far already. Here are some examples.\n\n```toml\n[empty_paren]\nmatch='(:[1])'\nrewrite='()'\nrule='where nested'\n```\n\nThis transform matches any content between balanced parentheses (including\nnewlines) and deletes the content. The `:[1]` is a variable that can be used in\nthe rewrite part. By default, `comby-reducer` will try to apply this\ntransformation at the top-level of a file, wherever it sees `(...)`. The\n`rule='where nested'` tells `comby-reducer` that it should also attempt to\nreduce nested matches of `(...)` inside other matched `(...)`. In general,\nparentheses are a common syntax to nest expressions in programs, so it makes\nsense to add `rule='where nested'`.\n\nAnother transform preserves the first comma-separated element inside\nparentheses:\n\n```toml\n[preserve_first_paren_element]\nmatch='(:[1],:[2])'\nrewrite='(:[1])'\n```\n\nProgram syntax often use call or function-like syntax that comma-separate\nparameters or arguments inside parenthes. This transformation attempts to remove\nelements in such syntax. This transform doesn't have a `rule` part, since it\nmight not be as fruitful to attempt nested reductions inside of `:[1]` or\n`:[2]`. But, we could easily add it.\n\nA last example uses a special form `:[var:e]` which matches \"expression-like\"\nsyntax.\n\n```toml\n[remove_first_expression_for_colon_sep_space]\nmatch=':[1:e], :[2:e]'\nrewrite=':[2]'\n```\n\nExpression-like syntax matches contiguous non-whitespace characters like `foo`\nor `foo.bar`, as well as contiguous character sequences that include valid code\nblock structures like balanced parentheses in `function(foo, bar)` (notice how\nwhitespace is allowed inside the parentheses). The transform above will attempt\nto remove expression-like syntax between commas, which often separate\nexpressions inside objects, records, or lists.\n\n**More info.** You can learn more about the underlying matching engine at\n[comby.dev](https://comby.dev/docs/basic-usage). You can try out\ntransformations on [comby.live](bit.ly/3lOmS4W) to check that a transformation\nbehaves the way you want it to.\n\n**Limitations and known issues.** \n\n- Although regular expression matching is possible with `:[...]` syntax in\n  [`comby`](https:/github.com/comby-tools/comby), it's **not yet possible to\n  write regular expression holes in `comby-reducer` transforms.**\n\n- Some inputs may trigger a stack overflow in `node`. Post a GH issue with the\n  input if you run into this.\n\n### Tips\n\n#### Customize crash criteria with scripts\n\n`comby-reducer` expects a program to exit with signal `139` or `134` to consider\nit a crash. Many programs that crash won't exit with these values, however. For\nexample, the [Solidity compiler](https://github.com/ethereum/solidity) exits\nwith a signal `1`. Even more challenging, the exit signal `1` may mean that the\nprogram crashes, or that the program doesn't compile (and we want the program to\nstill compile). The exit signal `1` is not a reliable way to know that the\nprogram crashed \"for real\". What to do?\n\nIt'll depend on your program, but you generally want to define some criteria that\nconstitutes a valid crash, and wrap that logic in a script. For Solidity, a valid program\nthat crashes the compiler will emit something like:\n\n```\nInternal compiler error during compilation:\n/solidity/libsolidity/ast/Types.h(797): Throw in function virtual std::unique_ptr\u003cReferenceType\u003e\n```\n\nWe can use this information in a script, and exit with the expected crash code\nto signal a crash. Here's one I used before, called `compile.sh`, that will exit the script with\nsignal `139` when it sees the `Internal compiler error` message:\n\n```bash\n#!/bin/bash\n\nRESULT=$(~/solidity/build/solc/solc $1 2\u003e\u00261)\nMATCH=$(echo $RESULT | grep -c \"Internal compiler error\")\nif [ $MATCH == 0 ]; then\n        exit 0 # no match, program doesn't cause expected crash\nfi\n\nexit 139\n```\n\nYou can get very fancy with your script, and can use it further refine program\nreduction. For more inspiration read up on [interestingness tests](https://embed.cs.utah.edu/creduce/using/) covered by\n[C-reduce](https://github.com/csmith-project/creduce).\n\n#### Output\n\nOutput the final reduced program by piping the `comby-reducer` command to a\nfile. The final program is sent to `stdout`, the informative messages are\nprinted to `stderr`.\n\n### Options\n\nSome additional command line flags:\n\n`--record` is an optional flag that emits the program at each step of a\nsuccessful reduction, in the form `\u003cnum\u003e.step`, in the current directory. You\ncan replay the transformations by running `comby-reducer-replay` in the current\ndirectory. See more on [comby-reducer-replay](#comby-reducer-replay) below.\n\n`--lang \u003cextension\u003e` is a flag that determines how the source file is\n  parsed. Using an extension like `.c` or `.go` will make `comby-reducer` parse\n  the input according to that language.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003eclick to expand the list of accepted extensions\u003c/summary\u003e\n\n```\n.s        Assembly\n.sh       Bash\n.c        C\n.cs       C#\n.css      CSS\n.dart     Dart\n.dyck     Dyck\n.clj      Clojure\n.elm      Elm\n.erl      Erlang\n.ex       Elixir\n.f        Fortran\n.fsx      F#\n.go       Go\n.html     HTML\n.hs       Haskell\n.java     Java\n.js       JavaScript\n.jsx      JSX\n.json     JSON\n.jsonc    JSONC\n.gql      GraphQL\n.dhall    Dhall\n.jl       Julia\n.kt       Kotlin\n.tex      LaTeX\n.lisp     Lisp\n.nim      Nim\n.ml       OCaml\n.paren    Paren\n.pas      Pascal\n.php      PHP\n.py       Python\n.re       Reason\n.rb       Ruby\n.rs       Rust\n.scala    Scala\n.sql      SQL\n.swift    Swift\n.txt      Text\n.ts       TypeScript\n.tsx      TSX\n.xml      XML\n.generic  Generic\n```\n\n\u003c/details\u003e\n\n`--transforms \u003cdir\u003e` will use `.toml` transform definitions in the specified `dir`.\n\n`--debug` will emit the reduced program after each step, and the transformation that succeeded to `stderr`.\n\n## comby-reducer-replay\n\n`comby-reducer-replay` is the answer to \"How was my program reduced?\".\n`comby-reducer-replay` is installed along with `comby-reducer` and should be\navailable based on how you installed it.\n\nAfter running `comby-reducer` with `--record`, simply run `comby-reducer-replay`\nin the current directory, and step through the transformed program at each step\n(left and right arrow keys). Try running `comby-reducer-replay` inside\n[replay-example](./replay-example) to step through a recording of a previous\ncrash reduction for a Solidity compiler bug.\n\nBy default replays will use `git diff` to render changes. To override the\ndefault, a custom diff command can be entered on the command-line like this:\n\n```bash\ncomby-reducer-replay colordiff -y\n```\n\nwhere `colordiff -y` shows a side-by-side colored diff of changes. Underneath\nthe hood, the `.step` files will be appended to the command, like `colordiff -y\n000.step 001.step`\n\nSome sensible default flags are included for common diff tools, which you can\nexplore by entering only the name of the tool and no other extra command line\nflags:\n\n```bash\ncomby-reducer-replay git               # the default\ncomby-reducer-replay patdiff           # an enhanced patience diff tool\ncomby-reducer-replay colordiff         # colordiff, configured to render side-by-side\ncomby-reducer-replay diff              # plain old diff, configured to render side-by-side\n```\n\nI recommend installing [`patdiff`](https://github.com/janestreet/patdiff) for\nan enhanced viewing experience. `patdiff` simply understands diffs a bit\nbetter. To get `patdiff`, you'll have to:\n\n- [Install opam](https://opam.ocaml.org/doc/Install.html) with\n\n  `sh \u003c(curl -sL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)`\n\n- Run `eval $(opam env)`\n- Run `opam install patdiff`\n\nAnd `patdiff` should now be available on your path.\n\n## Development\n\n- Install `npm`.\n- Install `npx`.\n\n```\nnpm i typescript @types/node minimist @types/minimist @iarna/toml @types/iarna__toml\nnpx tsc\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomby-tools%2Fcomby-reducer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcomby-tools%2Fcomby-reducer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomby-tools%2Fcomby-reducer/lists"}