Suptiftam is an esolang created by User:PythonshellDebugwindow.
Suptiftam uses integers (signed and unbounded), 8-bit bytes (unsigned and wrapping), and tape-tapes—two-dimensional tapes unbounded in all four directions, initialized to all zeros—of either type (an individual tape-tape, however, cannot contain both types).
Functions in Suptiftam must take one argument and can return no result. They need not be pure.
Suptiftam has the following builtin functions, each of which takes a tape-tape as an argument.
downmoves its argument's head down
leftmoves its argument's head left
rightmoves its argument's head right
upmoves its argument's head up
includereads the 0th line of its argument as a null-terminated string and includes the file with that name, if there is one
It is undefined behavior if an integer or byte is passed to any of these functions.
Each statement must be on its own line. Spaces can only appear in function definitions' first lines, and even then only to separate identifiers; anywhere else, they will cause a fatal syntax warning.
Integer literals are written in base 14 (with capital letters) and parsed in base 23; for example,
10 would yield 23 and
1D would yield 36. Byte literals are written as a printable, non-whitespace (a space here will still result in a "warning") ASCII character between single quotes; for example,
'A' yields 65,
'\' yields 92 (note: there are no escape sequences in Suptiftam) and
'*' yields 42. There are no tape-tape literals; a variable must be made in order to construct a new one.
Identifier (function names, function argument names, and variable names) must be entirely alphabetical, in the extended Latin, Greek, and/or Cyrillic alphabets.
Function definitions are started by a line containing the keyword
fd, the function name, and the function's argument followed by a colon, all in no particular order. This means that
fd x y:,
x y: fd, and
y: fd x are all functionally the same. Between this line and the matching
fi (stolen from Bash), all code is the function's body.
Redefining a function doesn't overwrite the original, but instead appends the new body to the old one; this is called extending the function.
Function calls can be written almost as they would be in Python:
x. However, the four tokens (function name, both parentheses, and argument) can be written in any order, meaning that
():x:f are all the same.
Conditional function calls
Functions can be called conditionally by appending, prepending, or intrapending
if(y) (which is considered to be one token) to a function call. This calls the function only if
y is nonzero (if
y is a tape-tape, then compare the value under its head). For example,
:x:if(y)f (among 119 other combinations) calls
y (or the value under its head) is nonzero.
To declare a variable as an integer or a byte, write
varname~value; this detects the type of
value, creates a variable of that type called
varname, and sets it to
value. To declare a variable as a tape-tape, write
TYPE is either
byte, which, contrary to all logic, signify bytes and integers respectively.
To assign the value
value to the variable
varname=value. Assigning a value to a tape-tape assigns to the tape-tape's current cell instead of the tape-tape itself, and if the value to assign is a tape-tape, its current cell is used instead. If the types of the value and the variable do not match (or, if the variable is a tape-tape, the type of its cells), then the variable is left unchanged and a random digit is printed to the output terminal's current cell (see #I/O format).
The syntax to calculate
x op y is
%op[x]y%. Operations cannot be nested. For example, to find the sum of 6 and 36, you could write
%+1D%, which might be written as
6 + 36 in a more conventional language. Valid operators are
+ for addition,
- for subtraction, and
/ for division (you must make multiplication yourself).
If a tab appears anywhere in a line, that line is a comment.
There is a global scope. In addition, each function has its own scope. In case of conflict, the most global name is used. All functions, including nested ones, belong to the global scope, as all function definitions (including nested ones) are "hoisted", or brought to the beginning of the file, before the program begins.
Including a file means that its contents will be executed immediately in the global scope of the main file. The same file can be included multiple times.
Input and output are represented as separate tape-tapes, named
term (short for terminal).
read is read before the start of the program, and
term should be updated live (or at least printed after the program ends).
Integers with no digits
A could be the literal value 10, or it could be a variable. This, and other expressions like it, are parsed in the following way:
- If it is a variable, evaluate to the variable's value;
- otherwise, evaluate it as an integer literal.
This means that in the following program,
x will be equal to 10 and
y will be equal to 7.
x=A A=7 y=A
term='H' right(:term:) term='e' right(:term:) term='l' right(:term:) term='l' right(:term:) term='o' right(:term:) term=',' right(:term:) term=%-['_']'A'% right(:term:) term='w' right(:term:) term='o' right(:term:) term='r' right(:term:) term='l' right(:term:) term='d' right(:term:) term='!'
fd tmach x: term=x right(:term:) tmach(:x:)if(x) fi tmach(:%-[read]48%:)
Can't handle blank lines or null characters (ASCII zero).
fd cat :x term=read right(:term:) right(:read:) cat(:x:)if(:read:) eol(:x:)if(:%-read%:) fi fd eol :x down(:term:) down(:read:) setXNegOne(:term:) setXNegOne(:read:) right(:term:) right(:read:) cat(:x:)if(:read:) fi fd setXNegOne :tt left(:tt:) setXNegOne(:tt:)if(:tt:) fi cat(:0:)