*T
*T is an esoteric programming language created in 2017 by Anderson Antunes. It is designed as an extension of Brainfuck that maintains full compatibility while adding numerous operators and features to facilitate programming. *T introduces compact syntax through single-character operators, support for multiple numeric types (8, 16, 32-bit integers and 32-bit floats), string literals, variables through identifiers, mathematical operators beyond + and -, stack operations, function declarations, conditional statements (if-else), and debugging capabilities. The language emphasizes portability and is specifically designed to run in limited environments such as microcontrollers and portable devices, while offering a creative challenge through its "encrypted-like" code aesthetic where readability becomes part of the exploration process.
Hello World
"Hello, World!" PS
Features
- Numeric constants and string loading
- Mathematical, logical and bitwise operators
- Multi-line and single-line comments
- Support for unsigned 8, 16, 32-bit integers and 32-bit floats
- Structures like if-else and while with continue and break support
- Stack operators
- Function declaration and execution with reusable scope
- Designed to run in limited environments, such as gadgets and microcontrollers
- Dynamic execution of code stored in memory
- Flexible numeric type system with dynamic switching
- Debugging tools for analysis and execution in restricted environments
- Interpreter extensibility with external functions
- Compatible with runtime code generation and execution
- Compatible with Brainfuck codes
Basic concepts
The main element in common with bf is the idea of representing memory as a tape/array consisting of "cells"/bytes, reading and manipulating the values of this tape is done through a read head/pointer, which receives instructions from the source code and performs actions accordingly. There are instructions to move the tape forward or backward, selecting new regions, performing mathematical operations and controlling program flow.
Every program starts at position zero of memory and can select other positions using the >
and <
operators. Numeric constants can be used preceding the operators, if there is no constant in use the value 1 is assumed.
Example: move 3 bytes to the right
- in bf:
>>>
- in *T:
3>
The read head has some specific characteristics in *T, it stores a register capable of storing values, type size in use and results of logical operations. These values can be used to simplify some operations. When a numeric constant is processed, it stays stored in the register, and may or may not be moved to the memory tape using the !
operator. Similarly, values can be loaded into the register using ;
, or even swapped between register and memory using @
.
Different numeric types are supported in *T, integer types can have 3 sizes: 8, 16 and 32 bits, all unsigned. In addition to integer types, the 32-bit float type can also be used. By default the 8-bit integer type is used, to change the type you can use the operators: b
(byte - uint8), s
(short - uint16), i
(int - uint32) and f
(float).
When changing the numeric type in use, the displacement operators will move the tape by the size in bytes of each type: b
- 1 byte, s
- 2 bytes and 4 bytes for i
and f
.
Example: types and displacement
32!>
orb32!>
- stores 32 in the first byte and moves to the seconds256!>
- stores 256 using the first two bytes and moves to the thirdi20000!>
- stores the value using the first 4 bytes and moves to the fifthf3.1415!>
- stores the value using the first 4 bytes and moves to the fifth
For input and output the operators ,
and .
are used, respectively. ,
allows reading a character and storing it in the current position of the tape. .
prints the value of the current position as an ASCII character. Additionally in *T the PN
/PRINTNUM
function can be useful for printing the value in decimal.
Example: print the number 7
- in bf:
-[----->+<]>++++.
or (55 repetitions of+
with.
at the end) - in *T:
7+ PN
While in bf only the +
and -
operators are present, *T also has *
, /
and %
, and allows using numeric constants. This way some operations are simplified.
Example: "3 + 4"
- in bf:
+++ ++++
- in *T:
3+ 4+
Example: "2 * 3"
- in bf:
++[>+++<-]
- note that it's necessary to use a repetition loop to perform a multiplication, divisions are even more laborious to "emulate"
- in *T:
2+ 3*
or2@3*
(using switch) or2p3o*
(using stack operators)
Comments and Strings
A detail worth showing is the use of comments. When //
is found the program jumps to the next line break, and when /*
is found it jumps to the next */
, allowing comments with more than one line or comments within the same line.
Strings can be loaded into the tape using "
, when writing "Hello World"
, for each character, the corresponding value from the ASCII table is loaded, at the end the tape is moved to the beginning of the string, allowing the use of the PS
/PRINTSTRING
function to print the values in ASCII until reaching a value equal to zero. Optionally it's possible to use the >
operator at the end of the string in the form "Hello World">
to avoid returning to the beginning of the string and continue using valid values from the tape.
Example: Comments and Strings
- Comments until the end of the line:
// My first program "Hello World!" PS // End
- Comment with start and end:
/* My first program */ "Hello World!" /* String */ PS /* PRINTSTRING function */ /* End */
- Concatenates two Strings using identifiers:
S^ /* marks the start */ "Hello"> // goes to the end of the string and ignores the 0 value returning one position <" World!" S /* jumps to position S */ PS /* prints the string */
Control Flow
Repetition loops are defined by [
and ]
where repetition only happens when a logical operation with positive result was performed. Logical operations are performed by the ?
operator followed by the operation. Logical operations are always performed between the tape value and the register value.
Logical operators
t
- truth?>
- greater than?<
- less than?=
- equal?!
- different?l
- less than or equal?g
- greater than or equal??
- different from zero?z
- equal to zeroh
- true if stack is greater than zero
Conditionals can be used to execute code blocks if the logical operation is true or false (if/else) using (
if_true
)
or (
if_true
:
if_false
)
.
Example: Conditional and RAND function
- Generates a random number and compares with number 5, printing if it's greater or less/equal
RAND!10% ;PN 5?>(" > 5":" <= 5") PS
Variables
To simplify memory management, *T offers a way to map tape positions to a specific identifier, emulating the concept of variables. Identifiers consist of one or more uppercase letters, which can optionally be interspersed with underscores and numbers. Declaration is done using ID^
where ID is the identifier. After declaration, just use the identifier alone whenever you want to access the corresponding position.
An advantage when debugging code is that the type in use is also recorded (only in the debugger), allowing you to visualize the variable value more intuitively, especially when types different from the default (byte) are used.
Identifiers can also be used to call stdlib functions of the language, such as:
PN
/PRINTNUM
— prints a number.PS
/PRINTSTRING
— prints a string.RAND
— generates a random number.
Language Summary
Memory operations
<
- Move left by type size>
- Move right by type size!
- Copy register value to tape;
- Copy tape value to register@
- Swap tape value with register value and vice versaz
- Move 2 bytes to the right until finding a zero#
- Execute tape values as if they were commands until0
m
- Reallocate tape size based on current register
Data types
b
- 8bits unsigned (default)s
- 16bits unsignedi
- 32bits unsignedf
- float 32bits
Mathematical operations
+
- Add register value to tape value-
- Subtract register value from tape value*
- Multiply tape value by register value/
- Divide tape value by register value%
- Store remainder of tape value division by register value in tapew
- Introduces bitwise operations (&, |, ^, ~, <<, >>) based on active type
Input/Output
,
- Input character.
- Output characterPC
orPRINT
- Print register value as characterPS
orPRINTSTR
- Print current tape as stringPN
- Print register value as number
Comparisons
t
- sets the exclusive register value as true?>
- greater than?<
- less than?=
- equal?!
- different?l
- less than or equal?g
- greater than or equal??
- different from zero?z
- equal to zero?h
- true if stack is greater than zero~
- inverts the exclusive register value
Control structures
(
if_true
)
- If statement(
if_true
:
if_false
)
- If-else statement[
block
]
- While loopc
- continue (returns to the beginning of the loop)x
- break (ends the loop)
Stack operations
p
- Push (equivalent to!>
)o
- Pop (equivalent to;<
)h
- Store stack size in register
Functions
NAME
{
code
}
- Function declarationr
- Return from function
Comments
//
- Single-line comment/*
content
*/
- Multi-line comment
Examples
Fibonacci
9!>0!>1!?=[2<1-?!2>;<@>+] ;PN // Calculates the fibonacci of 9
String reversal
>,[>,]<[.<] // Reverses the input string
Mandelbrot fractal
The example below allows drawing the Mandelbrot fractal using ASCII characters:
s LL^24!>L^!> CC^70!>C^!> f CX^> CY^> TX^> TY^> TZ^> X^> Y^> bI^ sL 1- [ sCC;C! 1- [ b0I! f0X!Y! i0sCC;efCX!3/ i0sC;efCX@/ 2- i0sLL;efCY!2/ i0sL;efCY@/ 1- bI 10! [ f X;TX!* Y;TY!* 0TZ! TX;TZ+ TY;TZ+ 4?> ( x ) Y 2* X;Y* CY;Y+ TX;X! TY;X- CX;X+ bI 1- ?? ] bI;TZ! 38+ . sC 1- ?? ] b10TZ! . sL 1- ?? ]