Rivulet is a programming language of flowing strands, written in semigraphic characters. A strand is not pictographic: its flow does not simulate computation. There are four kinds of strands, each with their own symbolism and grammatical rules. Together, they form glyphs, tightly-packed blocks of code whose strands execute together.
Here is a complete Fibonacci program:
╵──╮───╮╭─ ╵╵╭────────╮
╰─╯╰──╯│ ╰─╶ ╶╮╶╮╶╯
╰─────╮ │ ╭─────╯ ╰─────╮
╰─╯ ╷ ╰─── ───╯╷
╵╵─╮ ╭─╮ ╭── ╵╵╰─╮ ──╮──╮
╰─╮│ ╰─╯ ╵╵╰─╯╶╮ ╴─╯ ╭─╯╭─╯
╰─╯╰─ ╰──╯╰────╯ ╭╴ ╵╶╯ ╶╯╶╮
╭─╮ ╭╴ │ ╰──────╯
│ │ │ ╰─╮ ╭─╮
│ │ ╰─╯ │ │ │
╰─╯ ╷ ╰──── ╰───╯╷
╵╵ ╭── ──╮ ╭─╮ ╵╰─╮
╰─╮ ╭─╯╭─╯ │ ╴─╯
╶╯╵╶╯ │ ╷╶╯ ╭─╮
╭─╮ ╰────╯ │ ╭─╮ │
│ ╰────╮ ╭─╯ ╭╴│ │ ╭─╯
╰────╮ │ │ │ │ │ │ │
╭────╯ │ │ ╰─╯ │ ╷ ╰─╷
╰────╮ │ ╰─────╯ │
│ ╰─────────╯╷
Here is the same program formatted by the interpreter into an svg, alongside two variations that produce equivalent computer instructions:
Status: Version 0.4. This is a mostly-working interpreter, and a tool to generate svg files of source code. The command list will likely need to expand for usability. |
Rivulet is a list-based language that avoids ordinary approaches to branching and looping. Strands never split and no strand is left un-executed.
Its writing system was inspired by the satisfying compactness of mazes, Anni Albers’s Meanders series, and space-filling algorithms. Its calligraphic aspects draw from natural language and favor the ability to write by hand.
In Rivulet, data is organized into lists of adjacent cells, populated with zeros by default. Commands are applied to either a single cell or an entire list. They take a second parameter, a constant or the value of another cell.
Commands can also be run list-to-list, applying the command to each successive cell of one list, from the corresponding cells of the other. While these consider zero-populated cells as well, a list-to-list command ends at the last cell holding a value in either list.
The first list, List 1, is sometimes used as the output stream. This is an interpreter setting, as is whether they are displayed as numerical data or a Unicode string (where each value is rounded to the nearest integer).
Every strand of every glyph runs in a Rivulet program; there is no equivalent of an “if” statement. If a glyph leads to an unwanted state, that glyph and the others of its block (all contiguous glyphs of the same level or higher), can be rolled back, setting the execution state to what it was before the glyph (or set of glyphs) fired. The conditional rollback is the only form of branching in Rivulet. Loops only end with a rollback of their last iteration. Tests for rollback are that a single cell or an entire list is either zero or non-zero, indicated by a special strand called the Question Strand.
Data strands are run in the order they begin at the top left, moving through each column flowing to the right. So the strand beginning at coordinate 2,0 is run, then 2,1, then 3,0, and so on. Question strands are always run after the data strands are executed.
Rivulet’s nuanced grammar may seem overwhelming at first but becomes easy to read and write with practice.
Glyphs begin with markers: ╵ in the upper left and end with ╷ at the bottom right. They must not have a vertically-oriented character directly above or below them, or they’ll be confused for strands. Any text outside of glyph markers is ignored.
The level of the glyph is marked by how many ╵s appear at the beginning of the glyph. Levels tell where glyphs fall within larger blocks of code.
Glyphs can be arranged vertically or side-by-side. They are read in the order of their starting marker location: top-to-bottom, left-to-right.
In other words, this program:
1 ╵╰──╮╰─ ╭──╯ ╶╮
2 ─┘ └─ │
3 ╭──────────┘
5 └──────── ╷
1 ╵╵ ╭───╮ ╭─
2 ╴─╮╶╯╶╮ ╷╶╯
3 ╵╰──┘ │
5 ╰───────╯
is identical to this one:
1 ╵╰──╮╰─ ╭──╯ ╶╮ ╵╵ ╭───╮ ╭─
2 ─┘ └─ │ ╴─╮╶╯╶╮ ╷╶╯
3 ╭──────────┘ ╵╰──┘ │
5 └──────── ╷ ╰───────╯
The interpreter refers to code locations in terms of glyph numbers and then line numbers. Line numbers reset to 1 in each glyph. After line 1, they are numbered for each successive prime. These numbers are semantically meaningful for some strands.
Other strand types use horizontal line numbers, counting in primes away from their starting hook. They always begin on line 1 and their neighbors are 2 on each side. They progress through primes, but always in distance from their starting point. Line numbers are every-other-line vertically: this is so vertical lines are not packed too tightly.
Here are examples of such strands:
╭─╮ ╭╴ ╭╴
│ │ │ │ │
│ │ ╰─╯ │ │
╰─╯ ╰─╯
5 3 2 1 1 2
Rivulet commands are written with these signs. Some re-use characters in a way that only context can disambiguate:
Name | Signs | Context |
---|
8 Comments
QuadmasterXLII
Is this what it feels like to learn to program as an adult? I can brainfuck and APL with the best of them, and this is the most alien language specification I’ve ever read
aa-jv
Truly beautiful code.
Looking forward to seeing this in graffiti form, sprayed all over some government building, giving the OCR-using hackers code to enter the (physical) backdoors that these scribbles are inevitably going to be indicating ..
vorbits
I would like to know how to even begin building an intuition for writing something in this. I read the entire README and the only thing I understand is glyph start and end ╵ ╷.
mrbluecoat
The Mayans would be proud
samchon
This is interesting, take my star
rottytooth
Hi, I created this language as part of a series of experiments with bringing aspects of natural language into code. My previous language, Valence (https://danieltemkin.com/Esolangs/Valence), dealt with semantic ambiguity — this one with calligraphy. It avoids an overly logical syntax in favor of compactness and expressiveness.
I’m completing a book of these esolangs for MIT Press this fall including this; not much info yet online yet but here’s the link: https://mitpress.mit.edu/9780262553087/forty-four-esolangs/
aredox
This is an interesting take on visual programming…
nathan_douglas
use a theremin as an input device