loopy

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.

Contents

Lexical stuff

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 \. \n, \t, \r, \b, \\, \", 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.

Program structure

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 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.

Data types and variables

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 1.0 and 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 0.0.

Memory management

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 [1]"" will cause your computer to run out of memory.) Memory can be freed either by calling free or by calling print (which frees the string it prints after printing it). Using memory after freeing results in undefined behavior, as does using memory in the same expression that frees it.

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 print (including ones that work just fine without the garbage collector) will likely have memory leaks. For another, calling free inside a larger expression that uses an array can cause problems.

Expressions

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:

Assoc.OperatorsMeaning/notes
unary prefix- function_nameNegation 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
right-to-left^Power
left-to-right* /Multiply, divide
left-to-right+ -Add, subtract
left-to-right? ! < > <= >=Compare. ? is equal, ! is not equal. All of these operate on numbers only.
unary prefix!!Not. I don't remember why it's two exclamation marks instead of one, but it is.
left-to-right& |And, or. They use short-circuit evaluation (i.e., they only evaluate their second argument if necessary) and return one of their arguments (like in JavaScript).
right-to-left=Assignment. Left side must either be a variable or a : expression. Returns the value assigned.

Built-in functions

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.

abs
Absolute value.
sin
Sine function. For cosine and tangent, you can use cos(x) = sin(π/2-x); tan(x) = sin(x)/cos(x)
atan
Arctangent (inverse tangent) function. asin(x) = 2*atan(x/1+sqrt(1-x*x)); acos(x) = π/2 - asin(x); pi = 4*atan(1).
sqrt
Square root
ln
Natural logarithm (base e). Log to base n is ln(x)/ln(n).
exp
Exponential (ex)
int
Truncates the fractional part of a number.
input
Reads input. If the argument is a number, reads a number and returns it, and the value of the argument is ignored. (If the user types something other than a number, the result is undefined.)
If the argument is an array, a string is read into the array and the array is returned. The first byte goes in ar:0, the second byte in ar:1, and so on (where ar is the argument). If ar gets full, reading stops and any more characters get read on the next input. If a newline or end-of-file is encountered before ar gets full, a 0 will be added after the last character read.
print
Prints the argument to the screen. If the argument is a number, prints that number. If the argument is an array, each value in the array is interpreted as an ASCII/whatever character code and printed until a character code 0 is encountered or the end of the array is reached, whichever comes first. If the argument is an array, the memory is also freed (unless using the garbage-collected version).
printkeep
Like print, but doesn't free memory.
array
Creates an array with the argument as its length.
length
Returns the length of an array.
free
In the non–garbage-collected version: if the argument is an array, frees it; if it's a number, does nothing.
In the garbage-collected version: ignores the argument and runs the garbage collector. (This means that free(0) runs the garbage collector in the garbage-collected version but does nothing in the non–garbage-collected version.)
islist
Returns 1 if the argument is an array; 0 if it's a number.

Interpreter

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).

The -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.