{"id":16334940,"url":"https://github.com/ncfavier/ni","last_synced_at":"2025-06-14T18:32:22.364Z","repository":{"id":66325172,"uuid":"301118978","full_name":"ncfavier/ni","owner":"ncfavier","description":"A stack-based concatenative programming language (and an IRC bot for some reason)","archived":false,"fork":false,"pushed_at":"2021-11-19T22:38:16.000Z","size":64,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T23:11:59.757Z","etag":null,"topics":["concatenative-language","stack-based"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ncfavier.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":"2020-10-04T12:02:06.000Z","updated_at":"2023-09-13T19:09:21.000Z","dependencies_parsed_at":"2023-03-10T23:49:27.206Z","dependency_job_id":null,"html_url":"https://github.com/ncfavier/ni","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ncfavier%2Fni","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ncfavier%2Fni/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ncfavier%2Fni/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ncfavier%2Fni/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ncfavier","download_url":"https://codeload.github.com/ncfavier/ni/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248233946,"owners_count":21069493,"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":["concatenative-language","stack-based"],"created_at":"2024-10-10T23:39:40.518Z","updated_at":"2025-04-10T14:30:35.061Z","avatar_url":"https://github.com/ncfavier.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `ni (a \u003c\u003e b) == ni a \u003c\u003e ni b`\n\nNi is a *stack-based*, *concatenative* programming language. Here's how it works:\n\nA **program** is a list of values.\n\nA **value** is either:\n\n- an integer: `42`,\n- a double: `3.14159`,\n- a boolean: `:true`, `:false`,\n- a character: `'c'`,\n- a string: `\"Hello, World!\"`,\n- a symbol: `foo`, `!@#\u0026*`,\n- a list of values: `[a b c ...]`,\n- or an environment.\n\nA program is **evaluated** by converting every value to an action and running the actions, one after another, inside a context.\n\nA **context** consists of a stack of values (**the stack**) and a stack of environments.\n\nAn **environment** is a map from symbols to actions.\n\nA **literal** value pushes itself onto the stack.\n\nEverything that is not a symbol is a literal value.\n\nA symbol prefixed with a backslash (`\\`) is literal (the backslash is removed).\n\nA symbol prefixed with a dollar sign (`$`) pops a literal value from the stack and binds it to that symbol (with the dollar sign removed) in the topmost environment. A dollar sign on its own pops a value from the stack without binding it to a symbol.\n\nAny other symbol has the action bound to it in the topmost environment in which it is bound.\n\n## Example\n\nThe following program prints the first ten Fibonacci numbers:\n\n    \\fib [ $n\n        0 1\n        [ $x $y x y x + ]\n        n times\n        const\n    ] define\n\n    0 $i [\n        i fib print\n        i increment $i\n    ] 10 times\n\n## Library\n\nThe following primitives are available.\n\nUnless otherwise specified, `b a`**`f`** means that `f` pops `a` from the stack, then pops `b` from the stack, then pushes the result onto the stack, if any.\n\n#### **`base`** environment\n\n- `action`**`eval`** evaluates `action` in the current context. Lists are evaluated as programs. Symbols are evaluated by making them into a singleton list, for convenience.\n- `name action`**`define`** binds the symbol `name` to the action of evaluating `action`.\n- `name`**`unbind`** unbinds `name` in the topmost environment.\n- `name`**`new`** creates a new empty environment with name `name`.\n- `env`**`use`** pushes `env` onto the stack of environments.\n- **`unuse`** pops an environment from the stack of environments and pushes it onto the stack.\n- `b a`**`=`** and `b a`**`/=`** are the usual equality and inequality functions, respectively. Equality is defined trivially on all values except environments: two environments are equal if they have the same name.\n- `a`**`not`**, `b a`**`and`** and `b a`**`or`** are the usual boolean operations.\n- `cond yes no`**`ifelse`** evaluates `yes` if `cond` is `:true`, else `no`.\n- `b a`**`+`**, `b a`**`-`**, `b a`**`*`**, `b a`**`/`**, `b a`**`%`** and `b a`**`^`** are the usual addition, subtraction, multiplication, division, modulo and exponentiation operations, respectively, defined on both integers and doubles. **`+`** is also concatenation on strings and lists.\n- `a`**`null?`** tests whether `a` is the empty string or list.\n- `vs v`**`cons`** appends `v` to the list or string `vs`.\n- `vs`**`uncons`** breaks the list or string `vs` into its head and its tail, pushes its tail onto the stack, then pushes its head onto the stack.\n\n#### **`io`** environment\n\n- `v`**`print`** prints `v` to standard output. Strings and characters are printed literally, other values are converted to their string representation.\n- **`printStack`** prints the stack, represented as a list, to standard output.\n- **`getChar`** reads a character from standard input.\n- **`getLine`** reads a line from standard input.\n- **`exit`** terminates the program successfully.\n\n#### Standard library\n\nThe file [`src/stdlib.ni`](https://git.monade.li/ni/tree/src/stdlib.ni) is evaluated when Ni starts.\n\n## Parsing\n\nComments are introduced with `#`, and extend to the end of the line.\n\nNumbers are parsed as an optional plus or minus sign, a (possibly empty) integer part, optionally followed by a dot and a (possibly empty) fractional part. One of the integer part or the fractional part must be non-empty. If a dot is present, the number is a Double, otherwise it is an Integer.\n\nCharacter and string literals are parsed as Haskell literals.\n\nSymbols are any sequence of non-whitespace, non-`[]` characters that fail to parse as any other type of value.\n\nEnvironments have an output representation (`\u003cenvironment NAME\u003e`), but no input representation.\n\n## Credits\n\nNi's syntax and core principles were largely suggested to me by [nitrix](https://github.com/nitrix).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fncfavier%2Fni","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fncfavier%2Fni","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fncfavier%2Fni/lists"}