{"id":15060485,"url":"https://github.com/nicbarker/river","last_synced_at":"2025-04-10T05:54:13.853Z","repository":{"id":44859213,"uuid":"198713543","full_name":"nicbarker/river","owner":"nicbarker","description":"River is an experimental assembly-like programming language.","archived":false,"fork":false,"pushed_at":"2022-05-30T00:22:20.000Z","size":802,"stargazers_count":89,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-03T07:12:38.646Z","etag":null,"topics":["assembly","assembly-language","experimental","programming-language","projectional-editor","structure-editor"],"latest_commit_sha":null,"homepage":"https://riverlanguage.org","language":"TypeScript","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/nicbarker.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}},"created_at":"2019-07-24T21:42:46.000Z","updated_at":"2025-03-24T10:39:35.000Z","dependencies_parsed_at":"2022-08-23T22:21:31.468Z","dependency_job_id":null,"html_url":"https://github.com/nicbarker/river","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicbarker%2Friver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicbarker%2Friver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicbarker%2Friver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicbarker%2Friver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nicbarker","download_url":"https://codeload.github.com/nicbarker/river/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248166927,"owners_count":21058480,"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":["assembly","assembly-language","experimental","programming-language","projectional-editor","structure-editor"],"created_at":"2024-09-24T22:59:23.455Z","updated_at":"2025-04-10T05:54:13.823Z","avatar_url":"https://github.com/nicbarker.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"River is an experimental assembly-like programming language.\n\nYou can view the current main branch at [https://riverlanguage.org](https://riverlanguage.org)\n\n\u003cimg width=\"1216\" alt=\"Screen Shot 2021-12-16 at 5 53 18 PM\" src=\"https://user-images.githubusercontent.com/2264338/146310470-41ea4153-2ca8-4cb9-b8a8-5f6447501c34.png\"\u003e\n\n### Overview\n\nRiver is a programming language that that has three core goals:\n\n- **Fast and easy compilation** - should be functionally instant on any modern machine for any program of any size, and easy to write your own compiler or backend with no programming language experience.\n- **Not text based** - the language is built up from atomic tokens not ASCII text and needs to be written using a special editor. The goal is to avoid a number of issues caused by ASCII text such as typos, arguments about formatting / indentation, name resolution, and compilation complexity.\n- **Customisable control flow** - the majority of the control flow and language structure is defined by the user as part of the program with the **macro** concept, instead of being built into the language.\n\n### Controls\n\nAt the moment River only supports keyboard interactions for the main editor.\n\n#### Cursor\n\nThe \"cursor\" is simply the currently highlighted instruction fragment.\n\n![Dec-22-2021 16-58-19](https://user-images.githubusercontent.com/2264338/147033316-733a2a6a-f44d-45f3-81c1-e8b3dbd98f24.gif)\n\nUse the **arrow keys** to move the cursor between instructions and fragments - keyboard shortcuts will apply to the current selection.\n\nThe `Enter` key is used to create a new instruction _after_ the current one, a lot like a newline in a text file. `Shift + Enter` creates a new instruction _before_ the current one.\n\n#### Auto complete\n\nWhen you see values separated by a vertical bar inside the cursor, this indicates that there are values available for auto completion.\nIn the case of a finite number of selections (such as instruction types) they are hotkeyed by the first letter.\n\n\u003cimg width=\"569\" alt=\"Screen Shot 2021-12-22 at 4 56 21 PM\" src=\"https://user-images.githubusercontent.com/2264338/147033150-93aa05b4-7dd2-4809-b8a2-4ecbaeb99da3.png\"\u003e\n\n_(the instruction types above would be hotkeyed with `s`, `d`, `a`, `c` etc)_\n\nIn the case of searchable values such as variable names, you can type alpha numeric keys to narrow down the list, and the `enter` key will select the first matched value.\n\n\u003cimg width=\"481\" alt=\"Screen Shot 2021-12-22 at 5 10 11 PM\" src=\"https://user-images.githubusercontent.com/2264338/147034181-315d4d98-a511-43c2-9608-aa9fb75e0565.png\"\u003e\n\n#### Selection\n\nYou can select multiple lines by holding the **shift** key and using the `up` and `down` arrow.\n\n![Dec-23-2021 07-47-22](https://user-images.githubusercontent.com/2264338/147140717-91162033-2eef-4477-a8f9-15953fc6868d.gif)\n\nOnce you've selected multiple lines, you can delete them all by pressing the `backspace` key, or create a new **macro** from the selected lines by pressing the `m` key.\n\n### Instructions\n\nAs an assembly like programming language, river has no control structures, no functions and a very small set of **instructions** for doing work.\nRiver is formatted with 1 instruction per line, and instructions are made up of `fragments` which are seperated by spaces.\ne.g the following is a `def` instruction, with three fragments:\n`def index 32`\n\nThe 5 basic instructions in river are:\n\n#### `def`\n\nusage: `def [variable name] [8 | 16 | 32 | 64]`\n\ne.g. `def index 32`\n\nUsed to define variables and indicate their size in **bits** on the stack. Currently there are only 4 sizes of unsigned int available to use, but eventually fragment 3 will be replaced with a `type`.\n\n#### `assign`\n\nusage: `assign [var] [= | + | - | * | / | %] [var | const]`\n\ne.g. `assign var 0 = const 5`\n\nUsed to modify a value defined with `def`. Note that this modification, like in assembly, happens _in place_. The original value is not preserved.\nFor example, `assign var 0 + const 1` would increment the variable at position 0 on the stack by `1`. The second operand can either be another variable or a constant value.\n\n#### `compare`\n\nusage: `compare [var | const] [= | != | \u003c | \u003c= | \u003e | \u003e=] [var | const]`\n\ne.g. `compare var 0 \u003c const 20`\n\nPerforms a mathematical comparison and only runs the next instruction if the comparison returns `true`. For example:\n\n```\ncompare const 10 \u003e const 20\nassign var 0 = 10\n```\n\nThe `assign` instruction here will never execute because `10 \u003e 20` will never be `true`.\n\n#### `scope`\n\nusage: `scope [open | close]`\n\ne.g. `scope open`\n\nScopes in river are used to define boundaries for memory management and `jump` instructions, and visibility of variables (they are lexical scopes). Any `def` instructions that occur after a `scope open` will be deallocated at the corresponding `scope close`.\n\n#### `jump`\n\nusage: `jump [start | end]`\n\ne.g. `jump start`\n\nThink `goto`, but safer and easier to use. `jump` either moves execution back to the beginning of the current `scope`, or to the end of the current `scope`. Used to construct control flow similar to `while` or `for`.\n\n### Value Fragments\n\nWhen an instruction needs to reference a **value**, such as in `compare` or `assign`, you can provide either:\n\n- a _variable_ with the keyword `var` followed by the variable's position on the stack, e.g. `var 0`\n- a _constant_ with the keyword `const` followed by the constant value (at this time only unsigned integers are supported) e.g. `const 5`\n  _Constants_ only ever exist in registers and aren't saved to main memory, whereas all `var` values are saved to memory.\n\ne.g. `compare var 0 \u003e const 10` tests whether the variable at position 0 on the stack has a value that is greater than 10.\n\nNote that the editor allows you to use \u003ca href=\"#inline-macros\"\u003einline macros\u003c/a\u003e to provide expression-line value fragments.\n\n### Memory\n\nRiver features an extremely basic memory management strategy. There is no dynamic allocation and no distinction between the stack and the heap - all defined variables are added to a linear growable memory structure at the beginning of their enclosing **scope**, and then are deallocated at the end of their enclosing **scope**.\n\n```c\nscope open // 16 bytes (2x 64 bit values) allocated here, placed on top of the stack\n    def foo 64 // var 0\n    def var 64 // var 1\n    assign var 0 = const 5\n    assign var 1 = var 0\n    assign var 1 + const 5\n    os stdout var 0\n    os stdout var 1\nscope close // 16 bytes deallocated here - highest 16 bytes removed from the stack\n```\n\nYou can imagine it simply like a giant stack inside a single function. This means that all variable sizes must be known at compile time - array sizes \u0026 string sizes must be declared, and there is no such thing as dynamically resizable structures.\n\nThis of course can feel restrictive, but it makes the code much easier for the compiler to reason about, and allows us to apply things like range checks at compile time.\n\n### Basic Code Structure\n\nThe actual river code in `.rvr` files isn't great to look at. There's no indentation, and variables are only referenced by their position on the stack.\n\nA program to print even numbers under 20:\n\n```c\ndef index 32 // Define a 32 bit variable at position 0 on the stack\ndef mod 32 // Define a 32 bit variable at position 1 on the stack\nassign var 0 = const 0 // index = 0\nscope open // Open a new scope to anchor jumps\nassign var 1 = var 0 // mod = index\nassign var 1 % const 2 // mod %= 2\ncompare var 1 == const 0 // if (mod === 0)\nos stdout var 0 // print(index)\nassign var 0 + const 1 // index += 1\ncompare var 0 \u003c const 20 // if (index \u003c 20)\njump start // jump to the most recent scope open\nscope close // close the scope and wind back the stack pointer\n```\n\nThe same program in the river editor with highlighting, indentation and variable names, with the output from the executed program on the right:\n\n\u003cimg width=\"1105\" alt=\"Screen Shot 2021-12-16 at 3 40 35 PM\" src=\"https://user-images.githubusercontent.com/2264338/146298141-9967a663-1faa-41c3-92f8-828aef04c0f7.png\"\u003e\n\nIt looks better, but it's still reasonably painful and slow to write without the help of higher level language constructs such as a `for` loop.\nThe way that river provides these language features is through a concept called **macros.**\n\n### Macros\n\nMacros in river are essentially just a smart way of copy pasting code around. They have two main features:\n\n- When they appear in code, macros are [folded](https://en.wikipedia.org/wiki/Code_folding) down into a single line and represented by the name of the macro.\n- Macros can use a concept called \"placeholders\" which allow you to replace specific parts of instructions in the macro. They're kind of like function arguments.\n\nHere is an example of a standard macro defined in river which is called `for`, used to imitate the behaviour of a [for loop](https://en.wikipedia.org/wiki/For_loop) in other languages.\n\n\u003cimg width=\"533\" alt=\"Screen Shot 2022-01-22 at 12 36 20 PM\" src=\"https://user-images.githubusercontent.com/2264338/150613513-3a71b701-1ba5-44bc-ac1b-e76eea9eb9cc.png\"\u003e\n\nYou can see that the macro does a number of things, such as defining and incrementing variables. Notice the **placeholder** values denoted by the `_` and the purple highlighting. **Placeholders** are values that are exposed and replaceable even when the macro is folded. This macro also includes a **placeholder block** denoted by `_block` that allows you to add multiple instructions at that position.\n\nWhen we apply the `for` macro to our code, it first appears like this:\n\n\u003cimg width=\"605\" alt=\"Screen Shot 2022-01-22 at 12 37 27 PM\" src=\"https://user-images.githubusercontent.com/2264338/150613567-d9feb194-3c38-4df9-bb43-176192d4f10c.png\"\u003e\n\nNotice how there are 3 exposed placeholder values after `for`? These function as replacements for the values you would expect in a standard loop of the format:\n\n`for (let i = initial; i \u003c max; i += increment) {`\n\nBelow the folded macro line there are braces `{` and `}` indicating that there is a block placeholder, and you can see line `5` is exposed for you to place code into.\n\nHere is our previous code to print even numbers under 20, but this time using the `for` macro:\n\n\u003cimg width=\"1019\" alt=\"Screen Shot 2022-01-22 at 12 40 31 PM\" src=\"https://user-images.githubusercontent.com/2264338/150613772-4b7db953-3c67-40b6-b732-dc21a950a6c9.png\"\u003e\n\nThis is better, but it still feels a bit awkward to have to pre-declare a variable to hold `mod` and use multiple `assign` instructions. This is where we can use **inline macros** to condense things even more.\n\n### Inline Macros\n\nStandard macros in River like `for` are folded down into their own line, and wrap their contents in a `scope` so that they don't expose any variables to outside code.\n\n_Inline Macros_ define a variable in the outer scope, and are rendered _inline_ in place of a **variable fragment.**\n\nAs an example, here is the definition of a very simple `expr` macro that can be used to construct _expressions_:\n\n\u003cimg width=\"508\" alt=\"Screen Shot 2022-01-22 at 1 02 56 PM\" src=\"https://user-images.githubusercontent.com/2264338/150615109-c5f4f7bb-3c42-4ec6-9f6d-1e16d4517aab.png\"\u003e\n\nAnywhere that you can use a variable fragment, such as `assign` or `compare` or even in the placeholder of another macro, you can use an inline macro.\n\nHere is an example of using the `expr` inline in place of a variable fragment in an `assign` instruction.\n\n![Jan-22-2022 13-06-35](https://user-images.githubusercontent.com/2264338/150615378-4b7499fd-ec2b-4809-99fc-ac164553619b.gif)\n\nHere is how you would write a standard expression assignment of `aVariable = 2 + 3` from another programming language.\n\n![Jan-22-2022 13-07-01](https://user-images.githubusercontent.com/2264338/150615437-1fb2a812-119f-4ff4-82ae-1a9faf57527e.gif)\n\nYou can even nest inline macros inside eachother.\n\n\u003cimg width=\"717\" alt=\"Screen Shot 2022-01-22 at 1 09 03 PM\" src=\"https://user-images.githubusercontent.com/2264338/150615505-338d4180-f645-48b6-9c43-1bafb96c0073.png\"\u003e\n\nAs a result, we can now take our program that printed even numbers under 20, and condense it even further using inline macros.\n\n\u003cimg width=\"967\" alt=\"Screen Shot 2022-01-22 at 1 11 51 PM\" src=\"https://user-images.githubusercontent.com/2264338/150615671-e5f6448c-0736-4af8-95ba-feaab8719bba.png\"\u003e\n\nIt's starting to look a lot more like a programming language. You can see that we've saved quite a few lines by using the `escape` key to toggle macro folding on and off.\n\n![Jan-22-2022 13-14-00](https://user-images.githubusercontent.com/2264338/150615797-6b079b06-9186-4209-bc70-66d169fc1921.gif)\n\n### Running your code\n\nYou can run your .rvr code in two ways:\n\n- The editor has a built in \"virtual machine\" which you can use to execute your code and see the output, in the `VM` tab.\n- The `Assembly` tab provides live output of the corresponding assembly for your chosen target architecture and platform. At the moment river compiles to [nasm](https://www.nasm.us/) assembly, but I'm not sure if it'll stay like that in future.\n\nCurrently supported platforms:\n\n- x64 (Windows)\n- x64 (Mac OS X)\n- WebAssembly\n\nYou can see the corresponding assembly instructions highlighted in the asm tab as you're browsing the code:\n\n![Dec-16-2021 16-38-53](https://user-images.githubusercontent.com/2264338/146304092-a9fb1ad3-0753-4cf2-9336-f8f9fbd9a664.gif)\n\n### Running WASM\n\nRunning WASM is a bit of a pain, but you can test out your code here:\nhttps://webassembly.github.io/wabt/demo/wat2wasm/\n\nUsing the following javascript:\n\n```js\nconst wasmInstance = new WebAssembly.Instance(wasmModule, {\n  console: { log: (num) =\u003e console.log(num) },\n});\nconst { untitled } = wasmInstance.exports;\nuntitled();\n```\n\n### Future\n\nRiver is currently full of bugs and incomplete functionality. Some of the next features for development are:\n\n- Examples (project euler or something)\n- Saving / loading files\n- WebAssembly browser / wasmtime flavours\n- Types (float, signed, bool first)\n- Structs or something similar to structs\n- Arrays and potentially tuple types\n- More standard macros\n- Binary rvr file representation\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicbarker%2Friver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnicbarker%2Friver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicbarker%2Friver/lists"}