{"id":18286723,"url":"https://github.com/xrstf/rudi","last_synced_at":"2026-01-13T18:12:48.750Z","repository":{"id":206695822,"uuid":"717454748","full_name":"xrstf/rudi","owner":"xrstf","description":"A Lisp-like, embeddable, leightweight, non-Turing complete language to transform JSON datastructures","archived":true,"fork":false,"pushed_at":"2025-03-16T16:16:09.000Z","size":1084,"stargazers_count":8,"open_issues_count":4,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-13T17:30:11.319Z","etag":null,"topics":["embeddable-scripting-language","go","json","lisp","programming-language","yaml"],"latest_commit_sha":null,"homepage":"","language":"Go","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/xrstf.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}},"created_at":"2023-11-11T14:36:34.000Z","updated_at":"2025-07-22T19:03:24.000Z","dependencies_parsed_at":"2023-11-21T09:43:10.373Z","dependency_job_id":"9ec7cd8e-a6db-4100-8518-0b29dd6d32e2","html_url":"https://github.com/xrstf/rudi","commit_stats":null,"previous_names":["xrstf/corel","xrstf/otto","xrstf/rudi"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/xrstf/rudi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xrstf%2Frudi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xrstf%2Frudi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xrstf%2Frudi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xrstf%2Frudi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xrstf","download_url":"https://codeload.github.com/xrstf/rudi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xrstf%2Frudi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28394873,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["embeddable-scripting-language","go","json","lisp","programming-language","yaml"],"created_at":"2024-11-05T13:21:52.129Z","updated_at":"2026-01-13T18:12:48.732Z","avatar_url":"https://github.com/xrstf.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Migration note\n\n\u003e [!IMPORTANT]\n\u003e Rudi has been migrated to [codeberg.org/xrstf/rudi](https://codeberg.org/xrstf/rudi).\n\n---\n\n## Rudi\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/rudi-portrait.png\" alt=\"\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/github/v/release/xrstf/rudi\" alt=\"last stable release\"\u003e\n\n  \u003ca href=\"https://goreportcard.com/report/go.xrstf.de/rudi\"\u003e\n    \u003cimg src=\"https://goreportcard.com/badge/go.xrstf.de/rudi\" alt=\"go report card\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://pkg.go.dev/go.xrstf.de/rudi\"\u003e\n    \u003cimg src=\"https://pkg.go.dev/badge/go.xrstf.de/rudi\" alt=\"godoc\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nRudi is a Lisp-like, embeddable programming language that focuses on transforming data structures\nlike those available in JSON (numbers, bools, objects, vectors etc.). Rudi programs consist of a\nseries of statements that are evaluated in order:\n\n```lisp\n(if (gt? .replicas 5) (error \"too many replicas (%d)\" .replicas))\n(set! .spec.isAdmin (has-suffix? .spec.Email \"@initech.com\"))\n(map! .spec.usages to-lower)\n```\n\nRudi is great for making tasks like manipulating data, implementing policies, setting default values\nor normalizing data configurable.\n\n### Contents\n\n* [Features](#features)\n* [Installation](#installation)\n* [Documentation](#documentation)\n  * [Language Description](docs/language.md)\n  * [Type Handling](docs/coalescing.md)\n  * [Standard Library](docs/stdlib/README.md)\n  * [Extended Library](docs/extlib/README.md)\n* [Usage](#usage)\n  * [Command Line](#command-line)\n  * [Embedding](#embedding)\n* [Alternatives](#alternatives)\n* [Credits](#credits)\n* [License](#license)\n\n### Features\n\n* **Safe** evaluation: Rudi is not Turing-complete and so Rudi programs are always guaranteed to\n  complete in a reasonable time frame. You can add support for functions defined in Rudi code (i.e.\n  at runtime), but this is optional to allow a safe embedded behaviour by default.\n* **Lightweight**: Rudi comes with no runtime dependencies besides the Go stdlib.\n* **Hackable**: Rudi tries to keep the language itself approachable, so that modifications are\n  easier and newcomers have an easier time to get started.\n* **Variables** can be pre-defined or set at runtime.\n* **JSONPath** expressions are first-class citizens and make referring to the current JSON document\n  a breeze.\n* **Optional Type Safety**: Choose between pedantic, strict or humane typing for your programs.\n  Strict allows nearly no type conversions, humane allows for things like `1` (int) turning into\n  `\"1\"` (string) when needed.\n* **Flexible**: The Rudi CLI interpreter (`rudi`) supports reading/writing JSON,\n  [JSON5](https://json5.org/), [YAML](https://yaml.org/) and [TOML](https://toml.io/en/).\n\n### Installation\n\nRudi is primarily meant to be embedded into other Go programs, but a standalone CLI application,\n`rudi`, is also available to test your scripts with. `rudi` can be installed using Git \u0026 Go. Rudi\nrequires **Go 1.18** or newer.\n\n```bash\ngit clone https://github.com/xrstf/rudi\ncd rudi\nmake build\n```\n\nThis will result in a `rudi` binary in `_build/`; to install system-wide, use `make install`.\n\nAlternatively, you can download the [latest release](https://github.com/xrstf/rudi/releases/latest)\nfrom GitHub.\n\n### Documentation\n\nMake yourself familiar with Rudi using the documentation:\n\n* The [Language Description](docs/language.md) describes the Rudi syntax and semantics.\n* All built-in functions are described in the [standard library](docs/stdlib/README.md).\n* Additional functions are available in the [extended library](docs/extlib/README.md).\n* [Type Handling](docs/coalescing.md) describes how Rudi handles, converts and compares values.\n\n### Usage\n\n#### Command Line\n\nRudi comes with a standalone CLI tool called `rudi`.\n\n```\nUsage of rudi:\n  -i, --interactive            Start an interactive REPL to run expressions.\n  -s, --script string          Load Rudi script from file instead of first argument (only in non-interactive mode).\n  -l, --library stringArray    Load additional Rudi file(s) to be be evaluated before the script (can be given multiple times).\n      --var stringArray        Define additional global variables (can be given multiple times).\n  -f, --stdin-format string    What data format is used for data provided on stdin, one of [raw json json5 yaml yamldocs toml]. (default \"yaml\")\n  -o, --output-format string   What data format to use for outputting data, one of [raw json yaml yamldocs toml]. (default \"json\")\n      --enable-funcs           Enable the func! function to allow defining new functions in Rudi code.\n  -c, --coalesce string        Type conversion handling, one of [strict pedantic humane]. (default \"strict\")\n  -h, --help                   Show help and documentation.\n  -V, --version                Show version and exit.\n      --debug-ast              Output syntax tree of the parsed script in non-interactive mode.\n```\n\n`rudi` can run in one of two modes:\n\n* **Interactive Mode** is enabled by passing `--interactive` (or `-i`). This will start a REPL\n  session where Rudi scripts are read from stdin and evaluated against the loaded files.\n* **Script Mode** is used the an Rudi script is passed either as the first argument or read from a\n  file defined by `--script`. In this mode `rudi` will run all statements from the script and print\n  the resulting value, then it exits.\n\n    Examples:\n\n    * `rudi '.foo' myfile.json`\n    * `rudi '(set! .foo \"bar\") (set! .users 42) .' myfile.json`\n    * `rudi --script convert.rudi myfile.json`\n\n`rudi` has extensive help built right into it, try running `rudi help` to get started.\n\n##### File Handling\n\nRudi can load JSON, JSON5, YAML and TOML files and will determine the file format based on the\nfile extension (`.json` for JSON, `.json5` for JSON5, `.yml` and `.yaml` for YAML and `.tml` /\n`.toml` for TOML). For data provided via stdin, `rudi` by default assumes YAML (or JSON) encoding.\nIf you want to use TOML/JSON5 instead, you must use the `--stdin-format` flag.\n\nThe first loaded file is known as the \"document\". Its content is available via path expressions like\n`.foo[0]`. All loaded files are also available via the `$files` variable (i.e. `.` is the same as\n`$files[0]` for reading, but when writing data, there is a difference between both notations; refer\nto the docs for `set` for more information). Additionally the filenames are available in the\n`$filenames` variable.\n\nAdditional raw files can be loaded using the `--var` flag: To load files, the format for this flag\nis `ENCODING:file:FILENAME`, for example `--var \"myvar=yaml:file:config.kubeconfig\"`. This allows\nyou to load files regardless of their extension and also allows to load raw files (that will be\nkept as strings) using `\"myvar=raw:file:logo.png\"`. Raw file encoding is not supported for files\ngiven as arguments, those files must have a recognized file extension.\n\n#### Embedding\n\nRudi is well suited to be embedded into Go applications. A clean and simple API makes it a breeze:\n\n```go\npackage main\n\nimport (\n   \"fmt\"\n   \"log\"\n\n   \"go.xrstf.de/rudi\"\n   \"go.xrstf.de/rudi/pkg/coalescing\"\n)\n\nconst script = `(set! .foo 42) (+ $myvar 42 .foo)`\n\nfunc main() {\n   // Rudi programs are meant to manipulate a document (path expressions like\n   // \".foo\" resolve within that document). The document can be anything,\n   // but is most often a JSON object.\n   documentData := map[string]any{\"foo\": 9000}\n\n   // parse the script (the name is used when generating error strings)\n   program, err := rudi.Parse(\"myscript\", script)\n   if err != nil {\n      log.Fatalf(\"The script is invalid: %v\", err)\n   }\n\n   // evaluate the program;\n   // this returns an evaluated value, which is the result of the last expression\n   // that was evaluated, plus the final document state (the updatedData) after\n   // the script has finished.\n   updatedData, result, err := program.Run(\n      context.Background(),\n      documentData,\n      // setup the set of variables available by default in the script\n      rudi.NewVariables().Set(\"myvar\", 42),\n      // Likewise, setup the functions available (note that this includes\n      // functions like \"if\" and \"and\", so running with an empty function set\n      // is generally not advisable).\n      rudi.NewSafeBuiltInFunctions(),\n      // Decide what kind of type strictness you would like; pedantic, strict\n      // or humane; choose your own adventure (strict is default if you use nil\n      // here; humane allows conversions like 1 == \"1\").\n      coalescing.NewStrict(),\n   )\n   if err != nil {\n      log.Fatalf(\"Script failed: %v\", err)\n   }\n\n   fmt.Println(result)       // =\u003e 126\n   fmt.Println(updatedData)  // =\u003e {\"foo\": 42}\n}\n```\n\n### Alternatives\n\nRudi doesn't exist in a vacuum; there are many other great embeddable programming/scripting languages\nout there, allbeit with slightly different ideas and goals than Rudi:\n\n* [Anko](https://github.com/mattn/anko) – Go-like syntax and allows recursion, making it more\n  dangerous and hard to learn for non-developers than I'd like.\n* [ECAL](https://github.com/krotik/ecal) – Is an event-based system using rules which are triggered by\n  events; comes with recursion as well and is therefore out.\n* [Expr](https://github.com/antonmedv/expr), [GVal](https://github.com/PaesslerAG/gval),\n  [CEL](https://github.com/google/cel-go) – Great languages for writing a single expression, but not\n  suitable for transforming/mutating data structures.\n* [Gentee](https://github.com/gentee/gentee) – Is similar to C/Python and allows recursion, so both\n  to powerful/dangerous and not my preference in terms of syntax.\n* [Jsonnet](https://github.com/google/go-jsonnet) – Probably one of the most obvious alternatives\n  among this list. Jsonnet shines when constructing new elements and complexer configurations\n  out of smaller pieces of information, less so when manipulating objects. Also I personally really\n  am no fan of Jsonnet's syntax, plus: NIH.\n* [Starlark](https://github.com/google/starlark-go) – Is the language behind Bazel and actually has\n  an optional nun-Turing-complete mode. However I am really no fan of its syntax and have not\n  investigated it further.\n* [Go Templates](https://pkg.go.dev/text/template) – I really don't like Go's template syntax for\n  more than simple one-liners. I liked and copied its concept of ranging over things, as templates\n  do not allow unbounded loops (just like Rudi), but apart from being safe to embed, Go templates do\n  not offer enough functionality to modify a data structure. Like Jsonnet, templates shine when\n  creating/outputting entire _new_ documents.\n\n  _Bonus mention:_ Mastermind's [sprig](https://github.com/Masterminds/sprig) served as inspiration\n  for quite a few of the functions in Rudi.\n\n### Credits\n\nRudi has been named after my grandfather.\n\nThanks to [@embik](https://github.com/embik) and [@xmudrii](https://github.com/xmudrii) for enduring\nmy constant questions for feedback :smile:\n\nRudi has been made possible by the amazing [Pigeon](https://github.com/mna/pigeon) parser generator.\n\n### License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxrstf%2Frudi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxrstf%2Frudi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxrstf%2Frudi/lists"}