loopy is an older esoteric programming language that I made, inspired by some IOCCC entry I'd seen where the entire program was done in one big loop. It also "solves" the halting problem by not allowing programs to halt.
Comments are between single quotes.
'This is a comment.' Whitespace forces tokens to be separate than otherwise wouldn't be, but otherwise doesn't matter; newlines are treated as whitespace.
Variable names can contain any ASCII letter, digit, or underscore, can't start with a digit, and are case-sensitive. Names of built-in functions can't be used as variable names.
String literals are surrounded by double quotation marks (
"") and can contain escapes starting with
\", and octal (but not hexadecimal) escapes are supported and mean the same thing as in C.
\c represents the ASCII escape character (hex 1b/oct 033) followed by
[ (used for many terminal commands). If a backslash is followed by anything else, the backslash is ignored. String literals can contain unescaped newlines. If a string literal is over 65535 characters, the behavior is undefined.
A loopy program is a sequence of statements. Each statement has a single condition in brackets (not optional), followed by a single expression, which is evaluated if the condition is true; that is,
[condition] expression. This is essentially an
if statement, except that you can't have multiple statements in the body, and you can't nest them (although you can use
| (and and or) to achieve a similar effect). There is no character to mark the end of the statement except for the starting
[ of the next statement; if you try to separate statements with a semicolon, Bad Things will happen.
Statements are evaluated in order, starting from the beginning, and after the last statement, the program starts over from the first statement. That is, the entire program is enclosed in an implicit infinite loop. This is the only way to make a loop in this language; there are no explicit loops, gotos, recursion, etc.
There are two data types: numbers and arrays (also called lists). Numbers are all floating-point. Arrays can contain numbers, other arrays, or both. Arrays have a length that's determined when they're allocated, which can't be changed without allocating a new array; an array's length can be determined at runtime using the
length function. Arrays are treated as references; if the same array is assigned to multiple variables, updates in one variable are visible in the other variables as well.
Booleans are represented by the numbers
0.0; when a number is interpreted as a boolean, anything other than
0.0 is treated as true. Strings are represented by arrays of numbers between 0 and 255 in whatever encoding the terminal uses; strings are terminated by the character 0 or by the end of the array, whichever comes first.
If an array is used where a number is expected, the array's length will be used instead.
Variables are all global, don't need to be declared, and can contain numbers or arrays (or both at different times). All variables start with the value
By default, loopy does not have garbage collection and requires manual memory management. Every time an array is allocated or a string literal expression is evaluated, memory is allocated that must be freed. (This means that a program like
"" will cause your computer to run out of memory.) Memory can be freed either by calling
free or by calling
There is also a garbage-collected interpreter but it has a couple issues. For one, the garbage collector only runs when
free is called (with any argument, including a number); this means that programs that just don't free any memory or only free memory with
loopy expressions are fairly normal. They can contain variables, number and string literals, and operators, and precedence can be overridden with parentheses. The following lists the operators in order of precedence:
|unary prefix||Negation and built-in function call (see below)|
|left-to-right||Get a value from an array; the array is on the left, index is on the right|
|unary prefix||Not. I don't remember why it's two exclamation marks instead of one, but it is.|
|right-to-left||Assignment. Left side must either be a variable or a |
loopy has a few built-in functions. You cannot define your own functions. Each function takes a single argument, which doesn't actually need parentheses around it.
2*atan(x/1+sqrt(1-x*x)); acos(x) = π/2 - asin(x); pi =
ar:0, the second byte in
ar:1, and so on (where
aris the argument). If
argets full, reading stops and any more characters get read on the next input. If a newline or end-of-file is encountered before
argets full, a 0 will be added after the last character read.
free(0)runs the garbage collector in the garbage-collected version but does nothing in the non–garbage-collected version.)
Interpreter written in C (source only; run
make to compile).
Keep in mind that there is undefined behavior that can in theory do anything; don't run untrusted programs in this interpreter, and definitely don't put it on a server where anyone can run programs on it.
The interpreter takes one or more files as arguments. If you specify multiple files, the interpreter will run them one after another if the program doesn't exit before that, which it always does (making this a completely useless feature).
-s option to the interpreter stops the program if it goes an entire loop without any of the conditions evaluating to true. This goes against the philosophy of the programming language, but it keeps the Control-C button from wearing out. (Note that programs that have side effects in their conditions might end too early with this option.)
This version has been modified slightly from the original that I made in 2007 to fix some bugs and make it compatible with more recent compilers. This version does not cause a bus error when encountering a semicolon in the source code, unlike the original version, because I was worried modern compilers would just optimize it out or something.