{"id":17028024,"url":"https://github.com/slotthe/rq","last_synced_at":"2025-04-12T12:10:32.706Z","repository":{"id":221019802,"uuid":"752699857","full_name":"slotThe/rq","owner":"slotThe","description":"Functional command-line JSON processor with a bidirectional type system","archived":false,"fork":false,"pushed_at":"2025-03-08T10:46:36.000Z","size":336,"stargazers_count":25,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-26T06:51:08.198Z","etag":null,"topics":["jq","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/slotThe.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":"2024-02-04T15:11:48.000Z","updated_at":"2025-03-08T10:46:39.000Z","dependencies_parsed_at":"2024-05-01T13:52:46.558Z","dependency_job_id":"e5c2b1ea-c102-41eb-a687-f51cf49bfe85","html_url":"https://github.com/slotThe/rq","commit_stats":null,"previous_names":["slotthe/rq"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slotThe%2Frq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slotThe%2Frq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slotThe%2Frq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slotThe%2Frq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slotThe","download_url":"https://codeload.github.com/slotThe/rq/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248565074,"owners_count":21125417,"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":["jq","rust"],"created_at":"2024-10-14T07:52:02.711Z","updated_at":"2025-04-12T12:10:32.686Z","avatar_url":"https://github.com/slotThe.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rq\n\n`rq` is a tiny functional language with which you can manipulate JSON.\nBasically, it is (an insignificant subset of!) [jq], written in Rust.\n\n![Usage example](https://github.com/xmonad/xmonad-contrib/assets/50166980/3ba78375-b7de-4ca1-8dd5-e930fee0e85d)\n\n[jq]: https://github.com/jqlang/jq\n\n**NOTE**: This project is in its very early stages;\nlot's of essential functions—and perhaps even syntax—might be missing,\nand overall I can't guarantee that anything actually works.\nUse at your own risk :)\n\n## Installation\n\nUse `cargo install`.\nFor nix users, a dev-shell is provided by the flake; one can access it with `nix develop`.\nAdditionally, you can run `rq` directly from the git repository:\n\n``` console\n$ nix run github:slotThe/rq\n```\n\n## Usage\n\nCall `rq` with an expression, and pipe some JSON into it!\n\n``` console\n$ cat test.json\n[{\"name\": \"John Doe\", \"age\": 43, \"phones\": [\"+44 1234567\", \"+44 2345678\"]}]\n\n$ cat test.json | rq '\\x -\u003e x.0.phones.1'\n+44 2345678\n```\n\nSome more usage examples:\n\n``` console\n$ cat simple.json\n[{\"name\": \"John Doe\", \"age\": 43, \"phone\": +44 1234567\"},{\"name\":\"Alice\"},{\"name\":\"Bob\", \"age\":42}]\n\n$ cat simple.json | rq 'map .name'\n[\"John Doe\",\"Alice\",\"Bob\"]\n\n$ cat simple.json | rq 'map .age | foldl (+) 0'\n85\n\n$ cat simple.json | rq 'filter (get \"age\" | (\u003e= 42)) | map (\\x -\u003e { x.name: x.age })'\n[{\"John Doe\":43},{\"Bob\":42}]\n\n$ cargo metadata --format-version=1 | rq '.packages | map .name'\n[\"ahash\", \"aho-corasick\", \"allocator-api2\", \"anyhow\", \"ariadne\", \"cc\", \"cfg-if\", \"chumsky\", \"hashbrown\", \"libc\", \"memchr\", \"once_cell\", \"proc-macro2\", \"psm\", \"quote\", \"regex-automata\", \"regex-syntax\", \"rq\", \"serde\", \"serde_derive\", \"stacker\", \"syn\", \"unicode-ident\", \"unicode-width\", \"version_check\", \"winapi\", \"winapi-i686-pc-windows-gnu\", \"winapi-x86_64-pc-windows-gnu\", \"yansi\", \"zerocopy\", \"zerocopy-derive\"]\n```\n\n## The expression language\n\n- Constants: `null, false, true, 1, 2.6, \"string\"`.\n\n- Lambdas, which can be written in various ways:\n\n        \\x -\u003e x        |x| x        λx → x\n\n- Application is done via whitespace: `(\\x -\u003e x 1 2) const`.\n  This would be akin to\n\n        (|x| x(1, 2))(const)\n\n   in pseudo-Rust notation (`const` is a [builtin function](#builtin-functions)).\n\n- Binary operations:\n\n  - Arithmetic operations, with the usual precedence rules of `*` and `/` being preferred over `+` and `-`:\n\n          1 + 3 * 5 + 4 - 7    ≡    ((1 + (3 * 5)) + 4) - 7\n\n    Additionally `+` also concatenates strings.\n\n          \"furble\" + \"wurble\"    ≡    \"furblewurble\"\n\n  - Comparison operations:\n\n          1 = 3 * 5 + 4 - 7    ≡    1 = (((3 * 5) + 4) - 7)\n\n          1 \u003c 4 + 5 = 5        ≡    (1 \u003c (4 + 5)) = 5\n\n  The following table details the precedence rules:\n\n    | Op                              | Precedence |\n    |---------------------------------|------------|\n    | `*`, `/`                        | 3          |\n    | `+`, `-`                        | 2          |\n    | `=`, `!=`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=` | 1          |\n\n- If-then-else expressions:\n\n        if 5 = 2 + 3 then \"wurble\" else 4  ≡  if (5 = (2 + 3)) then \"wurble\" else 4\n                                           ≡  \"wurble\"\n\n- Arrays: `[1, 3, null]`.\n  Arrays can contain arbitrary expressions:\n\n        λx → [1, get 0 x, if false then 1 else 5]\n\n- Objects: `{ \"this\": 3, \"that\": null }`.\n  Apostrophes can be omitted:\n\n        { this: 3, that: null }    ≡    { \"this\": 3, \"that\": null }\n\n  In fact, keys can be arbitrary expressions—just make sure they actually evaluate to something sensible!\n\n        { if true then \"this\" else \"thus\": 3, that: null }\n          ≡  { \"this\": 3, \"that\": null }\n\n- Expressions can be type-annotated with `::` or `∷`, though this is usually not necessary.\n\n        λ\u003e 1 + 2 :: JSON\n        3\n\n        λ\u003e :e 1 + 2 + (3 ∷ JSON)\n        + (+ 1 2) (3 ∷ JSON)\n\n### Syntactic sugar\n\n- The `get` function—with which one can index arrays and objects—can be abbreviated by `.`:\n\n        λx → x.0.this     ≡    λx → get \"this\" (get 0 x)\n\n        (λx → x.0.this) [{this: 4}]    ≡    4\n\n  Additionally, `.0` is sugar for `(|x| x.0)`.\n  This composes sanely:\n\n        .0.1.2  ≡  λx → get 2 (get 1 (get 0 x))\n\n  Note that this syntax is only available if the to-be-indexed-thing is a variable.\n\n        [1, 2, 3].0     # Parse error!\n\n- Instead of manually composing functions, `|` may be used instead;\n\n        (get 0 | λx → { x.id: x.name }) [{id: 42, name: \"Arthur\"}, 4]\n          ≡  { 42: Arthur }\n\n- Lambdas can be written taking multiple arguments,\n  in which case they are automatically curried:\n\n        \\x y -\u003e x    ≡    \\x -\u003e \\y -\u003e x    ≡    |x, y| x\n\n- A shadowed variable may be accessed using its De Bruijn index:\n\n        λ\u003e (λx → λx → x@2) 1 2\n        variable not in scope: x@2\n        λ\u003e (λx → λx → x@1) 1 2\n        1\n        λ\u003e (λx → λx → x@0) 1 2\n        2\n        λ\u003e (λx → λx → x  ) 1 2\n        2\n\n- Various binary operators can be written in pettier/alternative ways:\n\n  - Multiplication: `*`, `·`\n  - Division: `/`, `÷`\n  - Equality: `=`, `==`\n  - Non-equality: `!=`, `/=`, `≠`\n  - Less-or-equal: `\u003c=`, `≤`\n  - Bigger-or-equal: `\u003e=`, `≥`\n\n### The type system\n\n`rq` is a statically typed language with subtyping.[^1]\nThe type system looks as follows:\n\n- Primitive types: `JSON`, `Num`, and `Str`, where `Num, Str ≤ JSON`.\n\n- Universal quantification: `∀a. «Type»` or `forall a. «Type»`.\n  The type variable `a` can be any valid identifier (that is not already a primitive type)\n\n- Function types: `«Type» → «Type»` or `«Type» -\u003e «Type»`.\n  Function types are contravariant in their first, and covariant in their second argument.\n\n- List types: `[«Type»]`.\n  Lists are covariant.\n\n### Standard library\n\n- Numerical operators:\nn\n  ``` agda\n  (+)  : JSON → JSON → JSON  -- Also works for string concatenation\n  (-)  : JSON → JSON → JSON\n  (*)  : JSON → JSON → JSON\n  (/)  : JSON → JSON → JSON\n  ```\n\n- Comparisons:\n\n  Essentially, everything that is not `false` or `null` is considered truthy.\n\n  ``` agda\n  (=)  : JSON → JSON → JSON\n  (!=) : JSON → JSON → JSON\n  (\u003c)  : JSON → JSON → JSON\n  (\u003c=) : JSON → JSON → JSON\n  (\u003e)  : JSON → JSON → JSON\n  (\u003e=) : JSON → JSON → JSON\n  ```\n\n- Higher order functions:\n\n  ``` agda\n  -- `map f xs` applies `f` to every \"value\" in `xs`, which may be an\n  -- array (in which case value means element), or an object (in which\n  -- case it really means value).\n  map : (JSON → JSON) → JSON → JSON\n\n  -- Like map, `filter p xs` applies `p` to every value of `xs`.\n  -- Keep the elements for which the predicate returns truthy.\n  filter : (JSON → JSON) → JSON → JSON\n\n  -- Left-associative fold over an array or (values of an) object; e.g.,\n  --\n  --   foldl f init [x₁, x₂, …, xₙ]  ≡  f(f(…f(init, x₁), …), xₙ)\n  --\n  foldl : (JSON → JSON → JSON) → JSON → JSON → JSON\n  ```\n\n- Misc\n\n  ``` agda\n  -- The identity function.\n  id    : ∀a. a → a,\n\n  -- Return the first argument\n  const : ∀a. ∀b. a → b → a\n\n  -- `get i x` gets the i'th thing out of x. I should be (evaluate to) a\n  -- number or a string, with x evaluating to array or object, respectively.\n  get   : JSON → JSON → JSON\n\n  -- `set i x` coll sets the i'th thing in coll to x, where i\n  -- should be a number or a string, and coll should evaluate to\n  -- an array or object.\n  set   : JSON → JSON → JSON → JSON\n  ```\n\n## REPL\n\nA REPL is provided for getting familiar with the language;\ncall `rq` with a `repl` positional argument:\n\n``` console\n$ rq repl\nλ\u003e\n```\n\nBy default, expressions will first be type-checked,\nand then evaluated as far as they can:\n\n``` console\nλ\u003e 1 + 2\n3\n\nλ\u003e |x| x\nλx'. x'\n\nλ\u003e \\x -\u003e x x\nOccurs check: can't construct infinite type: b ≡ b → c\n\nλ\u003e \\x -\u003e ids x\nvariable not in scope: ids\n\nλ\u003e (get 0 | λx → { x.id: x.name }) [{id: 42, name: \"Arthur\"}, 4]\n{ 42: Arthur }\n```\n\nAdditionally, the following keywords are available:\n\n  - Pretty-print the expression given\n    (this just runs the parser, followed by the pretty-printer):\n    `:e`\n\n        λ\u003e :e \\x -\u003e x x\n        λx. (x x)\n\n        λ\u003e :e \\x -\u003e get 0 x + 3 * 5 - 7\n        λx. (- (+ (get 0 x) (· 3 5)) 7)\n\n  - Type-check an expression, and print the type: `:t`\n\n        λ\u003e :t \\f -\u003e \\g -\u003e \\x -\u003e f x (g x)\n        (a → b → c) → (a → b) → a → c\n\n        λ\u003e :t \\x -\u003e get 0 x + 3 * 5 - 7\n        JSON → JSON\n\n        λ\u003e :t map\n        (JSON → JSON) → JSON → JSON\n\n        λ\u003e :t \\x -\u003e x x\n        Occurs check: can't construct infinite type: b ≡ b → c\n\n  - Get information on a builtin function with `:i`:\n\n        λ\u003e :i \u003c\n        e \u003c e' checks whether e is less than e'\n\n        λ\u003e :i map\n        map f xs applies f to every value in xs, which may be an\n        array (in which case »value« means element) or an object\n        (in which case it really means value).\n\n  - To list all builtin functions, use `:l`:\n\n        λ\u003e :i map\n        +\tAdd two number, or concatenate two strings.\n        -\tSubtract two numbers.\n        \u003c\te \u003c e' checks whether e is less than e'\n        … and so on …\n\n  - Debugging: `:d`\n\n        λ\u003e :d \\x -\u003e x 4 \"flurble\"\n        Lam(\"x\", App(App(Var(\"x\"), Const(Num(OrdF64(4.0)))), Const(String(\"flurble\"))))\n\n    + Prettier, yet more verbose, output: `:dp`\n\n            λ\u003e :dp \\x -\u003e x x\n            Lam(\n                \"x\",\n                App(\n                    Var(\n                        \"x\",\n                    ),\n                    Var(\n                        \"x\",\n                    ),\n                ),\n            )\n\n# Additional command line flags\n\nFor no reason at all, there are some additional command line flags;\nthey ostensibly have nothing to do with `rq`'s main functionality:\n\n  - `--flatten` (`-f`): Flatten the given JSON into a list.\n    Inspired by [gron](https://github.com/tomnomnom/gron).\n\n[^1]: This will be indicated by `SubType ≤ T`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslotthe%2Frq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslotthe%2Frq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslotthe%2Frq/lists"}