Forte is an esoteric programming language created by User:ais523 in 2006, based around redefinition of constants. Its syntax is similar to BASIC (a non-esoteric language). It is called Forte due to the mess it makes of the Peano postulates. It was created after considering the following pseudocode for the 99 bottles of beer program:
Start loop Output 99 " bottles of beer on the wall," Output 99 " bottles of beer." Output "Take one down, pass it around," Decrement 99 Output 99 " bottles of beer on the wall." Loop until 99 becomes 2 Output the last 2 verses (should that say 99 verses?)
Clearly, this is absurd; Forte does not have an explicit looping construct. In fact, it resembles SMITH, in that it does not allow any sort of jump, and loops by moving commands forwards (SMITH copies commands forwards).
The following commands exist in Forte (items in square brackets are optional):
- LET expression = expression
- PRINT expression [;]
- PRINT "string" [;]
- INPUT expression
- GET expression
- PUT expression
- REM comment
REM is a NOP, regardless of what follows it on the line (even a colon). Multiple commands can be written as one command by separating them with colons; if the last character on a line is a colon, the colon is respected but the line break is ignored. All commands must be preceded by a number (their line number); all numbers must be nonnegative integers (an implementation should allow unbounded integers). All whitespace, apart from newlines and whitespace inside strings, is ignored. For descriptions of the commands, see below.
Expressions take one of the following forms:
|Addition||(expression + expression)|
|Subtraction||(expression - expression)|
|Multiplication||(expression * expression)|
|Division||(expression / expression)|
Division is truncated downwards to the maximum integer not exceeding the result. A subtraction where the second number exceeds the first is undefined. The outermost pair of brackets in an expression may be omitted; all other brackets are compulsory (for instance, 1+(2+3) is allowed, but 1+2+3 isn't (and (1+2)+3 might have a substantially different value!)).
- LET. This command is the heart of the language, and is the only command that affects Turing-completeness. It redefines the number given by the expression on the left of the = sign to have the value of the expression on the right. (The expression's value at that moment is calculated; if it changes later, the number is still redefined to the expression's original value). Note that this permanently renders an integer unusable; see the examples below. (There are many cases left ambiguous by this description; the examples show what happens in each case.)
- PRINT. This outputs the value of the expression (as a number with no surrounding whitespace), or the given string. If the command does not end with a semicolon, a newline is output immediately afterwards.
- INPUT. This works like LET, except that the redefinition of the number is taken from the input, not from an expression. The expression gives the number to redefine.
- GET. This works like INPUT, except that it inputs a character and redefines the number given by the expression to its ASCII code. It inputs 256 on end-of-file.
- PUT. This outputs the character whose ASCII code is given by the expression, without a newline following. (Of course, if the expression evaluates to 10 (newline), a newline is output.)
- REM. This never does anything; it is provided as a way to write comments.
- END. This causes the program to end. If this isn't given, the program enters an infinite loop rather than ending.
The program starts at the line with the lowest line number (thus, the lines can be given in arbitrary order at the start of the program). Whenever a command finishes, the command with the next lowest number is run, even if that command has run earlier. (Due to the fact that the values of the line numbers can be redefined, this can happen quite easily.) If a command's line number is redefined while the command is executing, even indirectly, this leads to undefined behaviour; if two lines' line numbers have the same value, the behaviour is also undefined. Again, the examples below should make things clearer.
All the examples here are part of one long program.
10 REM This is an example program: it is written for the Esolang wiki.
This does nothing (the colon negates the newline, but is ignored in turn by the REM; the 'it' is not taken as the start of a command).
20 PRINT 6*9
This outputs 54.
30 LET 9=7 40 PRINT 6*9
This now outputs 42, as 9 has the value 7.
50 LET 42=20 60 PRINT 6*9
This outputs 20 (the value of 42).
70 LET 20=75
After this command is complete, and before command 80 runs, 75 is printed. This is because the next command to run is the command labeled 20 (which has the value 75).
80 LET 42=9
This assigns 7 to 20 (42 and 9 have both been redefined). Both 42 and 20 now have the value 7 (as does the number 7); there is no way to use the integers 42 or 20 for anything; even something like
90 PRINT 4*5 : PRINT 6*9
outputs 7 twice. (This is what is meant by redefinition permanently rendering an integer unusable; it makes input to a Forte program incredibly difficult).
100PRINT (6*9)*11 :PRINT6* (9*11)
This outputs 77, followed by 462. This sort of thing is why bracketing is compulsory in Forte. The line shows that non-newline whitespace is ignored (for technical reasons, there are no form feeds, tabs, or vertical tabs in the above line, but there could be and the line would still work).
100110 LET 110=110+3 109 LET 100110=108 110 PRINT "Looping...": LET 108=108+3
This prints the output
many times; after all, lines 110, 113, 116, 119, etc., all say PRINT "Looping..."... The colon, although it cancels the line break, is not cancelled itself. Line 109 is used to start the loop off, and is not copied; line 108 (numbered 100110 at the start of the program to prevent it being run prematurely) is used to copy line 110. There have to be two renumbering lines like this because a line cannot renumber itself.
172 LET 114=95
This exits the loop (line 173 will output "Looping..." one last time), and 174 is substantially lower than 173 at this point.
180 PRINT "42"; 190 PRINT 42
This outputs "427". (The 42 inside the string literal is not a number, so is not redefined, and the semicolon omits the newline after the PRINT statement on line 180).
195 INPUT 60
This line could cause all sorts of havoc. In fact, the INPUT statement is probably best avoided. Who knows what values the user might enter?
Something needs to cause the program to end.
200 LET 200=201
The behaviour of this is undefined, as it redefines 200.
210 PRINT "210" 220 PRINT "220" 230 LET 210=220:LET 210=240
This leaves both 210 and 220 with the value 240 (220 is assigned to 210, then 240 is assigned to 220 (the value of 210)). When this line is actually reached, there is undefined behaviour.
250 LET 99 = 4 + 5
Commands cannot be split across lines, so this is a syntax error.
Here is a method for showing Forte's Turing-completeness, by Keymaker in 2014, by introducing a simple language Ectoforte and providing the means to translate it to Forte. Ectoforte is shown Turing-complete by providing a way to translate Minsky machine to it.
The design principles of Ectoforte were simple: to make a small Turing-complete language that would be as easy as possible to translate to Forte. Originally the language had more features that no doubt made programming easier, but these were removed in favour of simplicity. This is the simplest way the author came up with. It should be noted that there was no intention to make Ectoforte particularly unique or interesting in its own right; the name already tells much, it is a parasite language built upon Forte, taking all the advantage of the host it can.
A vital role in Ectoforte programming (and program execution) is the idea of cycles. A line in Ectoforte is a line of data that begins with the label, then ':', and then at least one instruction (and there is no upper limit to them). A line may be ON or OFF. During a cycle, each line that is ON will be executed (sequentially, from first to last). Likewise the instructions within a line are executed sequentially. On the very first cycle, all lines are ON.
The memory model of Ectoforte is counters/registers that can only be increased. They can be compared only in the context of setting lines ON or OFF, see below.
The instructions are:
- abc+++ increases register abc as many times as there are succeeding '+' characters (there needs to be at least one)
- xyz.ON sets line xyz on for the next cycle. It does not matter what state the line is, or on which cycle (unlike with OFF, see below). But setting the same line ON twice during the same cycle is forbidden.
- line3.OFF sets line3 OFF for the current cycle. A line must first be ON during the current cycle for it to be set OFF. Setting OFF a line that is currently OFF in the current cycle (or ON or OFF in some past cycle) or has been set ON for the next cycle during the current cycle is forbidden.
- line1.zz\a1 sets the line line1 (which first must be ON during the current cycle, and same restrictions as above to ON and OFF setting apply) to either OFF or ON. Which one, it depends on what the "equalizer" (\) returns, after comparing two registers (zz and a1 here). The first register (zz) must be smaller than the second or equal to it. It is an error if during the "equalizer" comparison the first register has a larger value than the second. If registers are equal, the line is set ON (for the next cycle), if they are not, the line is set to OFF (for the current cycle).
- "hello"; and "hello" would print "hello" with a new-line and without, respectively.
- exit halts/ends the program.
An Ectoforte-to-Forte translator written in Python can be found here: http://yiap.nfshost.com/esoteric/forte/ectoforte.py
A MM-to-Ectoforte translator written in Python can be found here: http://yiap.nfshost.com/esoteric/forte/mmecto.py
Note that running the final MM-to-Ectoforte-to-Forte program can take hours even for a relatively simple program!
A simple MM program to increase counter A to 3 and then decrease it till it is zero.
1 INC A 2 2 INC A 3 3 INC A 4 4 DEC A 4 5 5 HALT
The MM-to-Ectoforte translator provides the following:
1: 1next.ON 2next.OFF 3next.OFF 4next.OFF 5next.OFF inst1.OFF inst2.OFF inst3.OFF inst4.OFF inst4x.OFF inst4y.OFF inst5.OFF 1next: inst1.ON 2next: inst2.ON 3next: inst3.ON 4next: inst4.ON inst4x.ON 5next: inst5.ON inst1: regA+ 2next.ON inst2: regA+ 3next.ON inst3: regA+ 4next.ON inst4: inst4x.regAzero\regA inst4y.ON inst4x: inst4y.OFF 5next.ON inst4y: regAzero+ 4next.ON inst5: exit
There is line "1" that sets "1next" active for the next cycle (and therefore inactive for the current cycle) and all others OFF for the current cycle, one Xnext line for every MM instruction, and one (INC, HALT) or three (DEC) instX lines for every MM instruction.
Running the previous data through Ectoforte-to-Forte translator, the resulting Forte program is:
39 LET 40 = (39 + 18) + 1 40 LET 39 = 40 + 17 41 LET 42 = (40 + 18) + 2 : LET 43 = 43 - 18 : LET 44 = 44 - 18 : LET 45 = 45 - 18 : LET 46 = 46 - 18 : LET 47 = 47 - 18 : LET 48 = 48 - 18 : LET 49 = 49 - 18 : LET 50 = 50 - 18 : LET 51 = 51 - 18 : LET 52 = 52 - 18 : LET 53 = 53 - 18 42 LET 47 = (40 + 18) + 7 43 LET 48 = (40 + 18) + 8 44 LET 49 = (40 + 18) + 9 45 LET 50 = (40 + 18) + 10 : LET 51 = (40 + 18) + 11 46 LET 53 = (40 + 18) + 13 47 LET 19 = 19 + 18 : LET 43 = (40 + 18) + 3 48 LET 19 = 19 + 18 : LET 44 = (40 + 18) + 4 49 LET 19 = 19 + 18 : LET 45 = (40 + 18) + 5 50 LET 51 = (51 - 18) + (((20 - 2) / (19 - 1)) * 54) : LET 52 = (40 + 18) + 12 51 LET 52 = 52 - 18 : LET 46 = (40 + 18) + 6 52 LET 20 = 20 + 18 : LET 45 = (40 + 18) + 5 53 END
A Quine was discovered by Stack Exchange user ATaco, which, in retrospect is actually fairly simple.
20 LET 10=40 30 PUT 34 50 PUT 34 60 PUT 59 10 PRINT "20 LET 10=40 30 PUT 34 50 PUT 34 60 PUT 59 10 PRINT ";