X7

x7 (pronounced "except") is a stack-based pseudo-golfing language devised by User:LyricLy in 2020 and first implemented in 2022. While ostensibly a language meant to be used for golfing, x7's design does not optimize for byte count per se; instead, it is a proof of concept for an original paradigm of golf programming. As such, it makes some design decisions that are counter to the direct goal of golfing in order to support its novel concepts. x7's main draw is that its control flow is based on exceptions, called 'raises' in the language's documentation, and the ability for the program to fully rewind its state to certain points to retry blocks of code without affecting the program state. A focus on exceptions allows the programmer to more easily control edge cases and potentially save bytes by eliding explicit checks.
== Syntax ==
As x7 is stack-based, simple expressions are written as you'd expect in RPN:
 1 2 3*+
 > 7
Each instruction is one character long. Integer literals are written in decimal; spaces are needed to separate them.
But things get more complex: certain operators take other code as arguments, and they do this by parsing some number of blocks. Let's start things off simple with an operator that takes 1 block: T. T pops a number from the stack and runs its block that many times. Take a look:
 1 10T2*`
 > 1024
Here, we give T the code 2* as an argument, and it pops 10 from the stack, so it will double the 1 at the bottom of the stack 10 times, ultimately pushing 210.
Note that we used a backtick to end the block. Backticks work like closing brackets for the implicit opening brackets after each instruction that takes a block. Nesting calls to T is simple:
 1 2T2T2*``
 > 16
This nested loop doubles the 1 2×2 times, yielding 24.
There's an alternative to using backticks if you want to close many blocks at once. For example, consider this (somewhat contrived) scenario:
 0 10T10T10T1+```
 > 1000
I want to add 1 a total of 10*10*10 times by nesting T blocks, but I don't want to write all of those backticks to close the blocks. Luckily, there's a solution. Blocks can be demarcated with braces, which implicitly close all blocks at the end. So I can just write this:
 0{10T10T10T1+}
 > 1000
Wonderful! There's one extra trick here: if you're at top level (not nested inside any other blocks), you don't need to include opening braces or closing braces at the end of the file. x}y}z will be parsed the same as {x}{y}{z}.
Some instructions take multiple blocks as arguments, which changes the rules in one simple way. Let's imagine an example with e, an operator that works similarly to the try/except feature in many practical languages. It takes two blocks, runs the first one, then runs the second if and only if the first block raises an exception. Here's an example of using it without any shenanigans:
 e1 0D`2`
 > 2
Two backticks to close two blocks. The first block divides by 0, which raises, so the second block is executed instead. Now, if we use the rule from before about how braces work, then this code wouldn't make much sense:
 e1}2
 > 1
Where's the opening bracket? x7 has a rule in place to make } more useful with multi-block commands. If there is more than one block to be parsed, then instead of closing all blocks (which would make any subsequent blocks be empty) a closing brace will act as a separator between blocks. However, it still has the effect of immediately closing all other blocks, like so:
 0e10T10T10T1+}2
 > 1000
== Variables ==
Values can be stored in and retrieved from variables using the special operators : and ; respectively by writing the name of a variable after it:
 42:x 22:y ;x;y;x
 > 42 22 42
Variable names can be any single character. They won't collide with the names of instructions.
== Functions ==
The ; operator has another use: it can call user-defined functions. Every line in an x7 program is its own function. By default, only the last line is executed, but you can refer to lines by number with ; to execute them:
 3 4
 1 2;1
 > 1 2 3 4
== Data types ==
=== Rationals ===
Rationals are the only number type supported by the language. They are represented as fractions; a pair of arbitrary-size integers. When a function says it expects an integer, that isn't a separate type; it's just a whole rational. Rationals can be constructed with a combination of integer literals, the division instruction D, and the negation instructionN:
 1N2D
 > -1/2
=== Lists ===
Lists are what you think they are; big ordered collections of anything you can imagine. They can be constructed with the empty list instruction [, the concatenation instruction ., or the collection instruction ]. Here's an example:
 1 2.3.
 > [1, 2, 3]
Unlike in most dynamically-typed languages, lists are homogeneous, so you can't make a list that consists of incompatible types. The types of two values are said to be compatible if one of the following holds:
* They are both rationals
* They are both boxes
* They are both pairs, and their respective elements are of compatible type
* They are both lists, and at least one of them is empty
* They are both lists, and the elements inside them are of compatible type
=== Boxes ===
Boxes are a way to hide your values away. The only way to get one is to put a value in a box, and the only thing you can do with the box is unbox it again to get the value back:
 0B
 > &0
 b
 > 0
Boxes are an opaque structure. They make the value inside inaccessible without unboxing, and every box has a compatible type as far as lists are concerned.
=== Pairs ===
The ideal fixed-size heterogeneous collection, pairs are a simple combination of two values. They are constructed with ,:
 1 2,
 > (1, 2)
Unlike boxes, the type of pairs is not opaque, so you can't store pairs containing different types in a list (unless you box them first)
== Rewinding ==
Normally, if you write a try/except block in a language like Python, it might act something like this:
 x = 0
 try:
 x += 1
 x /= 0
 except:
 # x will be 1
In x7, not so: when a raise is caught in a block (with the exception of !), everything the block did is rewound, so any effect it had on the program before raising is completely forgotten.
== Masking ==
One instruction in the language, `m`, can mask raises to prevent them being caught. Here's an example using the e instruction introduced earlier:
 em1 0D``2`
Unlike the first example with e, this code will raise instead of pushing anything. However...
 eem1 0D``2``3`
This code will push 3. This is due to the mask, which prevents raises from being caught for one layer. You can think of it like this. Call normal raises Raise, and say there is some constructor Mask(e) for masked raises. Now when m catches some raise e, it wraps it in a mask and raises Mask(e) instead. When another operator tries to catch a Raise, and gets a Mask(e) instead, it unwraps the mask and raises e. This allows you to nest levels of matching to do things like this:
 eeemm1 0D```2``3``4`
This code creates a doubly-masked raise and puts it through 3 layers of catching. The third layer will successfully catch it, pushing 4. Careful use of masking can allow to create much more terse programs by controlling which raises are caught by what.
Unless otherwise specified, when an instruction says it "catches" raises, it will only catch unmasked raises. Masked raises will have one layer of masking removed and be propagated.
== Temporary groups ==
Usually, stack manipulation commands operate on single values. However, sometimes it is convenient to treat multiple stack values as a consecutive group. Values on the stack can be grouped together temporarily with the & command, which will pop two values (or existing groups) from the stack and connect them together. Here's an example:
 0 1&d
 > 0 1 0 1
Usually the d command would only duplicate the top value, but the & causes it to consider the 0 and 1 together and duplicate them at once.
Only certain commands respect grouping, mostly those related to stack shuffling. (The documentation for them will refer to "groups" instead of "values".) If a different command, like +, tries to pop a value in a group, its entire group (even if it contains more than 2 values) will be broken apart, and only a single value from it will be taken.
== Lenses ==
Perhaps the most intricate feature of the language, lenses are how pairs and lists are accessed. Focusing into a position or set of positions with lenses allows you to then query, update or remove the focused values. Here's an example:
 1 2.3.0n
 > [<1>, 2, 3]
 @
 > 1
The indexing lens n focuses on a particular index of a list. Here we selected the first element, then used the @ instruction to access the value. We can also do something else with the selected value, like replacing it with $:
 1 2.3.0n2137$
 > [2137, 2, 3]
Operators like @ and $ are referred to as enders, because they are used to finish off after selecting values with lenses.
You can also focus on the first and second values of pairs with h and t:
 1 0,h
 > (<1>, 0)
 0$
 > (0, 0)
But focusing isn't limited to single values. By default, most instructions will implicitly focus on all the values in a list, so we can use them like this:
 1 2.3.
 > [1, 2, 3]
 0$
 > [0, 0, 0]
The j operator will nest further:
 1 2.]3 4.].
 > [[1, 2], [3, 4]]
 0$
 > [0, 0]
 p1 2.]3 4.].
 > [[1, 2], [3, 4]]
 j
 > [[<1>, <2>], [<3>, <4>]]
 0$
 > [[0, 0], [0, 0]]
@ can't handle more than one value, so it'll raise if we try to use it on multiple values. Instead, we can use ] to get all the selected values:
 1 2.]3 4.].j]
 > [1, 2, 3, 4]
Lo! The list is flattened. The nesting of lenses goes further than this, because we can use multiple at once:
 1 2.]3 4.]].5 6.]7 8.].].
 > [[[1, 2]], [[3, 4]], [[5, 6], [7, 8]]]
 j
 > [[<[1, 2]>], [<[3, 4]>], [<[5, 6]>, <[7, 8]>]]
 j
 > [[[<1>, <2>]], [[<3>, <4>]], [[<5>, <6>], [<7>, <8>]]]
See what it did there? Lenses work off the focusing that is already present, allowing you to chain them to nest deeper into a structure. This lets us do things like flattening a nested list.
We can do similar with a list of pairs, using h to take the first element of each one.
 1 2,3 4,.h
 > [(<1>, 2), (<3>, 4)]
This is just the tip of the iceberg and there are many more lenses and enders to make use of. You can see what they all do in the table of instructions below.
=== Drilling ===
In the section above, we saw that instructions will often work implicitly on lists, like how h works on lists of pairs as well as single pairs.
This is because many lenses and enders will automatically apply the j operator for you before running, a process known as drilling. Instructions can have one of 5 approaches to drilling into lists:
* Never drilling at all.
* Drilling only when the value has not already been focused ("from top").
* Drilling only when the value has not already been focused by something other than h or t ("from pair").
* Drilling only when the value has not already been focused by something other than h, t or n ("from single").
* Drilling as much as possible ("to atom").
All lenses and enders will raise if they are given an unfocused value that is not a list, with the exception of h, t and ].
The strategy used by each instruction is documented in the section below.
== Instructions ==
This list is subject to change. x7 is still new and under active development.
Where instructions specify the type of their operands, assume that they will raise if the operands are of the wrong type. Operands are popped from the top of the stack; popping an empty stack raises.
=== Arithmetic ===
+add 	Add two rationals.
-subtract 	Subtract two rationals.
*multiply 	Multiply two rationals.
Qquotient 	Perform Euclidean division on two integers. Raises on division by zero.
Rremainder	Perform Euclidean modulo on two integers.
Ddivide 	Divide two rationals. Raises on division by zero.
Nnegate 	Negate a rational.
Jfloor 	Floor a rational.
Kceil 	Ceil a rational.
=== Comparison ===
<less than 	Take two values and raise if the bottommost is not less than the other. Collections are compared lexicographically.
Gnot less than 	Take two values and raise if the bottommost is not greater than or equal to the other.
=equals 	Take two values and raise if they are not equal.
/not equals 	Take two values and raise if they are equal.
>greater than 	Take two values and raise if the bottommost is not greater than the other.
Lnot greater than	Take two values and raise if the bottommost is not less than or equal to the other.
=== Collections ===
bunbox 	Take a box's contents.
Bbox 	Form a new box.
,pair 	Form a pair out of two values.
[list 	Push an empty list.
.concat	Given two lists of compatible type, concatenate them. Given a list and a value of compatible type with its contents, append it. Given a value and a list with elements of compatible type, prepend it. Given two elements of compatible type, form a double-element list with them.
iiota 	Push a half-open range from 0 to a given nonnegative integer.
=== Lenses ===
These instructions all take focused values as the bottommost argument, which they may drill into as specified in the Drilling section.
hheaddrills: to atom 	Focus on the first item of a pair.
ttaildrills: to atom 	Focus on the second item of a pair.
jjoindrills: from top 	Focus on the elements inside a list.
nnthdrills: no 	Take an nonnegative integer and focus on the nth value of a list. Raises if the index is out of bounds.
sselectdrills: no (arg 1)drills: from single (arg 2)	Like n, but for multiple indices. Take a view of integers and focus on the nth values of a list. Raises if any index is out of bounds.
wwheredrills: from single 	Take a block and run it on each focused value. Unfocus values for which the block raises.
=== Enders ===
These instructions all take focused values as the bottommost argument, which they may drill into as specified in the Drilling section, and return the updated value after making changes.
@onlydrills: from top 	Push the only focused value. Raise if there is less than or more than one focused value.
]enlistdrills: no 	Push all focused values as a list. If given an unfocused value, wrap it in a list.
ccountdrills: from single 	Push the number of values focused.
$setdrills: from top 	Take a value and set all focused values to it, pushing the updated value.
X86drills: from pair 	Remove the focused values.
Ppastedrills: both from pair	Take a view and replace the focused values from the first view with ones from the second, removing any missing values from the end.
uu-turndrills: from single 	Reverse the order of the focused values in place.
Mmapdrills: from top 	Takes a block. Run the block on each focused value and replace it with the result. If the block raises, the respective value will be removed. Raises if the type invariant of lists is broken.
Zzipdrills: both from top 	Takes a second view. As M, but iterate over both views at once. The result has the shape of the first value. If the second view is too short, values from the end of the first view will be removed.
=== Loops ===
Wwhile	Takes a block. Run the block repeatedly until it raises.
Ffor 	Takes a block. Take a list. For each element in the list, push that element and run the block.
Mmap 	Takes a block. Take a list. For each element in the list, push that element and run the block. If the block raises, go to the next element; otherwise, pop a value. At the end, collect all values popped this way into a list and push it.
Ttimes	Takes a block. Take a nonnegative integer. Run the block that many times.
Ppick 	Takes a block. Take a list. For each element in the list, push that element and run the block. If the block raises, go to the next element; otherwise, stop.
=== Stack utilities ===
ddup 	Duplicate the group on top of the stack.
ppop 	Pop a value from the stack.
fflip 	Swap the places of the top two groups on the stack.
^yank 	Push the second group from the top.
_under 	Takes a block. Pop a group, run the block, then push the group again.
llift 	Takes two blocks. Run the second block, pop a group, rewind the state to before the block ran, run the first block, then push the popped group again.
~permute	Takes a block. For each possible permutation of values (not groups) on the stack, permute the stack that way, then run the block. If it raises, continue; otherwise, stop.
=== Raise manipulation ===
rraise 	Raises.
eexcept	Takes two blocks. Run the first block. If it raises, run the second block.
qquiet 	Takes a block. Run the block. If it raises, ignore it.
!invert	Takes a block. Run the block. If it raises, catch it without rewinding. If it finishes without raising, raise.
mmask 	Takes a block. Run the block. If it raises, mask and propagate it.
=== Debugging ===
vtrace 	Display the current state of the stack. Not subject to rewinding.
Vmonitor	Takes a block. Run the block and display a message if it raises, without catching. Not subject to rewinding.