REVER
REVER is a reversible programming language, which has a lot of things, but not quite everything.
Specification
Comments
Comments appear on a line (outside of a character or string literal) starting by # and the rest of the line is comments.
Expressions
The following binary operators are available the same as in C (including same priority):
- ^
- |
- &
- +
- -
- *
- / (division by zero result in "poison")
- % (modulo by zero result in "poison"; if right operand is positive, result is also always positive)
- <<
- >>
The binary operator $ is the INTERCAL bit interleaving operator, but resulting in "poison" if either operand is negative.
The binary operator ~ is used to reorder the bits in a number. (more description needed)
The binary operator ** is exponents. Anything (other than poison) to the power of zero is one (even zero to the power of zero is one). If the exponent is negative, the result is poisoned.
The C unary - and ~ is both usable, decimal and hexadecimal and octal integer constants are allowed same as C, and ASCII character constants as integers are allow as in C. You can also use ( and ) for grouping (although these also have another use; see the other section).
Integers are unbounded.
Any poisoned expression results in the statement it occurs in to be poisoned (in most cases this means that statement will have no effect).
A bijective expression in a variable consists of that variable exactly once, and any subexpression containing the variable it is in (including only the variable itself) cannot be used in a larger expression except as an operand to one of the following:
- The unary operators ~ or -
- The binary operators ~ (left only), -, +, or ^
- The index of a bijection
Variables
Variables may be declared at the parameter list of a subroutine, or may be declared at the beginning of the main routine (these names are local to the main routine, although the variables can be passed to subroutines).
Declare them by the type symbol, name, optionally the local name in parentheses if it is array (local name must be preceded by exclamation mark), equal sign, and an expression for the value, which cannot mention any variables except the local name in parentheses for the array index. This is initialized by the value of this expression.
Types:
- + Integer
- - Subroutine
- < Input stream
- > Output stream
- / Bijection (the last array index (there must be one) is the bijective variable; the initializer expression must be bijective in that variable)
Example:
+xyzzy(!x)=(x<<x)^(x-1);
If the initializer expression is poisoned, the value of that variable (or array element) is permanently poisoned.
You can even have arrays of arrays.
Although initializers can be an expression (or, for subroutines and streams, reference to that type), they may be arrays if so can be mentioned as follows: Have a list in square brackets, with an expression which cannot mention anything other than array indices, follows by equal sign, and the value, and list items are separated by commas. The first non-poison expression, the value is whatever value is on right side. If there is no match, the initial value is a null subroutine for subroutine type, or poisoned for other types.
You cannot declare variables of streams, although the type symbols can still be used in list of parameters of subroutines, and there are other ways to create streams.
Modifying
Modifying values of variables is restricted; you cannot simply assign a new value. However, the following operations are possible:
- v+=e; (where v is a integer variable and e is an expression which does not mention v): Add e to the value of v if e is not poisoned.
- v-=e; (where v is a integer variable and e is an expression which does not mention v): As above, but subtract.
- v^=e; (where v is a integer variable and e is an expression which does not mention v): As above, but bitwise exclusive-or.
- v~=e; (where v is a integer variable and e is an expression which does not mention v): As above, but using the bit reordering operator described in that section.
- v[x,y]; (where v is a integer variable, and x and y are expressions which does not mention v): If neither expression is poisoned, and the value of the variable is currently one of them, the value becomes the other one.
- v/b; (where v is a integer variable and b is a bijection; any array indices used to mention the bijection must not mention v): The value of the variable is affected by the bijection.
Bijections can be modified as follows:
- v[x,y]; (where v is a bijection variable, and x and y are expressions which does not mention v): If neither expression is poisoned, the index containing each value is swapped with the other.
- v/b; (where v and b are bijections, which are not allowed to mention each other): The values in v are affected by b.
- v(!x)=e; (where v is a bijection, x is a new name, and e is a bijective expression in an indexed v where the index is bijective in x and cannot mention v (the outside cannot mention x)): Values of the bijection v are affected using x as the input index and e as the new value. For example: stuff(!a)=-stuff(-a);
Variables using subroutines are affected as follows:
- v+=s; (where v is a subroutine variable and s is a subroutine (which can be a variable) which cannot mention v): Append s to v.
- v-=s; (where v is a subroutine variable and s is a subroutine (which can be a variable) which cannot mention v): Prepend s to v.
- v(x)=(y) (where v is a subroutine variable, x is a list of names for the parameters, and y is a list of the new assignments for parameters): In the list of names for the parameters, should have ! at first, and if they are arrays can optionally list any number of array indices each where each one is ! and a unique local name. Do not put the ! in the y list. Both lists must have the parameters of the correct types, and the same names must be listed on the right side, although they could be in a different order. If array indices are mentioned, they can also be reordered within the same array, but you must still only mention each one once each. The array indices can be bijective expressions in that index, and if the parameter (if an array, all indices specified) is integer or bijection type can also be bijective expressions in that parameter, where the expressions cannot mention the other parameters/indices.
You can also assign to array indexed values, where the array index on the left does not mention itself and the expression(s) on the right cannot mention the array at all.
You can affect all elements of a array by using () without an expression inside, or use (!x) where x is a local name. This local name must be unique within the statement but can appear zero or more times in any expressions including other array index expressions to the right of it.
For any two variables of the same type (including indexed, even indexed of the same array variable, although the indices cannot mention either side):
- x|y (where x and y are variables of the same type): The values are swapped. You are also allowed to swap poisons with non-poisons.
Macros
A macro is defined at the top level by putting the name, optionally parameters in parentheses (which must be plain names with no type prefix), and the replacement in braces. This is replacement by tokens, and the outer braces are not a part of the replacement.
If not all of the parameters are not given when used, the remaining parameter is automatically becoming the number of previous uses of any macros with parameters sharing names with the unspecified parameters.
Subroutines
The inverse of a subroutine is represented by putting . after its name. If it is a variable, the inverse is also treated as a variable.
A subroutine can be declared at the outer level of the program, by specifying the name, the list of parameters in parentheses which must include types (and if arrays, the number of indices as a integer constant after it), and then the statements within braces. A subroutine must take at least one parameter.
The main routine is declared without a name, and the parameters must be one input stream and one output stream (in that order). The main routine is optional.
Subroutines can call themself and other subroutines. The main subroutine is uncallable.
A subroutine call statement consists of the subroutine and then parameters in parentheses. These must be variables (which can be indexed (if required by the types), although the indices must not mention any of them), and must all be different (these variables can be parameters to this subroutine).
The parameters then reference the same variables which are passed to it, so the subroutine affects those same variables.
Teleports
A teleport statement starts with * followed by zero or more expressions separated by commas. If none of them are poisoned, jump forward to the next teleport within the same block with the same number of expressions and the same values of the expressions, wrapping around from the beginning of the block if necessary. This cannot access blocks inside of this one or blocks which this is inside of.
Streams
Streams are (usually finite) stacks of integers.
The inverse of a subroutine switches input streams with output streams and vice versa.
You can make a pipe with multiple subroutine calls separated by commas, where you can declare streams in the parameter lists (including arrays of streams) by using ! followed by a local name, and there must be exactly one input stream parameter and one output stream parameter in two different subroutines within the pipe for each name. Other than this, subroutines within a pipe are not allowed to share variables.
If something in a pipe sends output with nothing else in the pipe receiving inout, or one requests input but nothing is sending output to it, then it will get stuck.
The command y=x where x is a input stream and y is a output stream, to receive a single piece of data and send it.
The command y=x where x is a input stream and y is a array of integers, to receive a single piece of data and place it in index zero of the array, moving all nonnegative indices up one to make room.
The command y=x where x is a array of integers and y is a output stream, to send a single piece of data from index zero of the array, moving all positive indices down one afterward to erase the data from the array.
Bijections
The inverse of a bijection is represented by putting . after its name. The inverse is also a variable.
Examples
Truth-machine
(<i,>o) { +d(!x)=[0**x='1',0='0']; +x=0; d=i; *d(x); o=d; x-=1; *'0'; o=d; }
Add two numbers of input
(<i,>o) { +x()=0; +y()=0; x=i; y=i; x(0)+=y(0); o=x; }
Copy input to output
(<i,>o) { +x()=1/0; *x(0); o=x; x=i; *x(0); }