Thrillodendron is an esolang by User:BoundedBeans featuring object orientation completely contained within a string syntax.

Syntax[edit]

The entire program is stored within a quoted string (representing a method), and all data types are also represented as strings. Strings must be put in double quotes, single quotes do not work. This makes it pretty hard to actually do anything, unless we introduce escape sequences. Unlike most other languages, Thrillodendron uses the power sign (^) as the escape character, rather than the backslash. The backslash is completely usable as a normal character, but the power sign isn't. There are only three escape sequences. The two main ones are ^" (escaped double quote) and ^^ (escaped power sign). There is also a third, ^c(four digit decimal number), which takes four decimal digits (padded with zeroes if shorter, cannot be longer), interprets as a number, and ignores that many characters ahead of it. This allows for comments (up to 9999 characters, anything above that just insert another ^c and continue the comment, ideally not interrupting the flow of the text). This mainly will be used in the top level string, but you could use something like ^^c(whatever) to escape strings of deeper levels. Spaces, tabs, newlines, vertical tabs, carriage returns, and form feeds are useless to strings and will not be transcribed into the string, so they can be used for nice formatting. This does mean that these characters won't count towards the number of ignored characters for ^c, so be careful.

Data types[edit]

All data types are represented in a string syntax, but they are not all identical. They are all first class. The data types are:

Notes:

Method bodies[edit]

A method body is represented by a list (not the datatype, just a convenient word for it) of commands, ended by semicolons, the arguments preceded by colons. The commands are:

A:(variable reference):(value);

Sets the variable reference to refer to the value. This can also be used with an object instance value accessor to change instance values of an object, though it can only be used this way with settable values, not unsettable methods or classes, meaning the integers will always start with 1 if used in this manner. The value can be anything. The first argument is treated as a key rather than a value. However, in the second argument, putting a reference or accessor causes them to reference the same value (it does not cause a reference to a reference).

B:(value 1):(value 2):(variable reference);

If both values are integers, adds the two together and makes the reference reference the result. If the first is a list and the other is something else, append the value to the list instead and store. If the second is a list and the other is something else, prepend and store. If both are lists, concatenate them in the order they were put in as arguments. The third argument is treated as a key rather than a value.

C:(value 1):(value 2):(variable reference);

If both values are integers, does 1st - 2nd and stores. If the result is negative, store the absolute value. If the first is a list and the second is an integer, set the reference to the value at the integer index in the list. The third argument is treated as a key rather than a value.

D:(value 1):(value 2):(variable reference);

Like B and C, but multiply. Both values must be integers. The third argument is treated as a key rather than a value.

E:(value 1):(value 2):(variable reference);

Like B and C, but divide. Both values must be integers. The third argument is treated as a key rather than a value. If the second argument is zero, return 0.

F:(value 1):(value 2):(variable reference);

Like B and C, but modulo. Both values must be integers. The third argument is treated as a key rather than a value. If the second argument is zero, return 0.

B, C, D, E, and F can also use accessors instead of references.

G:(value 1);

Print the value as an integer if it is an integer. Print the value as a string of UTF-16 characters if it is a list containing only integers. Any type other than those or a reference or accessor to those will throw an error.

H:(variable reference);

Input an integer to the variable reference. The argument is treated as a key rather than a value. If there is no more input, put a zero.

I:(variable reference);

Input a line of input as a list of UTF-16 characters. The argument is treated as a key rather than a value. If there is no more input, put the empty list.

H and I can also use accessors instead of references.

J:(value);

If the value is zero or a reference or accessor to it, jump forward to the matching K.

K:(value);

If the value is anything except the integer zero or a reference or accessor to it, jump backward to the matching J. J and K match like parenthesis, and in most situations should have the same value checked but don't have to. Most likely you would want to check a reference or accessor here, but you could check 1 to make an infinite loop, or 0 to skip, or something like that.

L:(value):(variable reference);

If the value is a list of only integers, interpret it as UTF-16 character codes, evaluate it as a Thrillodendron literal, and set the reference to it. The literal does not need outer quotes or top-level escapes, but any inner values will. This can also use an accessor instead of a reference. The second argument is treated as a key rather than a value. This can be used with a method literal to execute code dynamically.

M:(value);

If the value is a method, run it. This can be used with an object accessor to run an object's methods.

N:(class):(variable reference);

Creates a new object of the class, stores it into the reference or accessor. The second argument is treated as a key rather than a value.

O:(variable reference):(variable reference)

Duplicates the value of the first reference or accessor and puts it in the second, rather than causing two references to the same value. This allows a value to be used more than once and have different modifications each time.

P:(value):(variable reference);

If the value is a list containing only integers, interpret it as UTF-16, use it as a filename to a Thrillodendron file, store the contents into the reference or accessor. Note that the content of the file specified does not need to be a method, it could be a class, list, or any other data-type, but it must be a literal. Non-method files cannot directly be executed.

Q:(value):(value):(variable reference);

Sets the reference or accessor to 1 if the two values are the same type, 0 if not. If both values are objects, it will return 1 if they are different classes, 2 if they have the same class or if their classes have the exact same content.

R:(value):(variable reference);

Sets the reference or accessor to the length of the first argument if the first argument is a list.


Examples[edit]

Hello world[edit]

"MG:^"L^^^"I72^^^",^^^"I101^^^",^^^"I108^^^",
^^^"I108^^^",^^^"I111^^^",^^^"I32^^^",^^^"I11
9^^^",^^^"I111^^^",^^^"I114^^^",^^^"I108^^^",
^^^"I100^^^",^^^"I33^^^"^";"

Truth-machine[edit]

"MH:^"VTruth^";J:^"VTruth^";G:^"I1^";K:^"VTruth^";G:^"I0^";"

Cat[edit]

"MA:^"V1^":^"I1^";J:^"V1^";I:^"VString^";G:^"VString^";K:^"V1^";"

Bitwise Cyclic Tag Interpreter[edit]

Accepts the initial queue, then the instructions, on separate lines.

"M

A:^"VQueueClass^":
^"C
^^^"L
^^^^^^^"L
^^^^^^^^^^^^^^^"I49
^^^^^^^^^^^^^^^"
^^^^^^^"
^^^"
^^^"L
^^^^^^^"M

 R:^^^^^^^^^^^^^^^"X
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"T^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"I10^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
  ^^^^^^^^^^^^^^^":^^^^^^^^^^^^^^^"VQueueLength_^^^^^^^^^^^^^^^";
A:^^^^^^^^^^^^^^^"VUsefulList_^^^^^^^^^^^^^^^":^^^^^^^^^^^^^^^"L^^^^^^^^^^^^^^^";
J:^^^^^^^^^^^^^^^"VQueueLength_^^^^^^^^^^^^^^^";
  C:^^^^^^^^^^^^^^^"T^^^^^^^^^^^^^^^":^^^^^^^^^^^^^^^"VQueueLength_^^^^^^^^^^^^^^^"
    :^^^^^^^^^^^^^^^"VCurrent_^^^^^^^^^^^^^^^";
  B:^^^^^^^^^^^^^^^"VCurrent_^^^^^^^^^^^^^^^":^^^^^^^^^^^^^^^"VUsefulList_^^^^^^^^^^^^^^^"
    :^^^^^^^^^^^^^^"VUsefulList_^^^^^^^^^^^^^^^";
K:^^^^^^^^^^^^^^^"VQueueLength_^^^^^^^^^^^^^^^";
O:^^^^^^^^^^^^^^^"VUsefulList_^^^^^^^^^^^^^^^"
    :^^^^^^^^^^^^^^^"X
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"T^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"I10^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
    ^^^^^^^^^^^^^^^";
^^^^^^^"
^^^"
^^^"L^^^"
^^^"^^^"
^"
;

A:^"VUsefulList^":^"L^";
I:^"VPreQueue^";
A:^"VQueue^":^"O^^^"VQueueClass^^^"^^^"VPreQueue^^^"^";
I:^"VCode^";
A:^"VCounter^":^"I0^";

J:^"I1^";
  C:^"VCode^":^"VCounter^":^"VCurrent^";
  C:^"VCurrent^":^"I49^":^"VSubCurrent^";
  J:^"VSubCurrent^";
    M:^"X^^^"VQueue^^^"^^^"I20^^^"^";
  K:^"I0^";
  C:^"VCurrent^":^"I48^":^"VSubCurrent^";
  J:^"VSubCurrent^";
    B:^"VCounter^":^"I1^":^"VCounter^";
    C:^"VCode^":^"VCounter^":^"VCurrent^";
    C:^"VQueue^":^"I0^":^"VFront^";
    C:^"VFront^":^"I48^":^"VFront^";
    J:^"VFront^";
      B:^"X^^^"VQueue^^^"^^^"I10^^^"^":^"VCurrent^":^"X^^^"VQueue^^^"^^^"I10^^^"^";
    K:^"I0^";
  K:^"I0^";
  B:^"VCounter^":^"I1^":^"VCounter^";
  R:^"X^^^"VQueue^^^"^^^"I10^^^"^":^"VQueueLength^";
  C:^"VCounter^":^"VQueueLength^":^"VTestCounter^";
  J:^"VTestCounter^";
    A:^"VCounter^":^"I0^";
  K:^"I0^";
K:^"I1^";
"