The most recent release of my Bare Bones interpreter made identifiers case-insensitive, and added an optional peephole optimizer that recognizes a common idiom:
while N not 0 do; Â decr N; Â incr X; end;
Such a loop adds N to X, clearing N in the process. Without the optimizer, this is has time complexity O(N). The optimizer replaces it with a single macro that executes in O(1). This makes the supplied example program that computes Fibonnaci numbers run much faster.
For the next release, I’ve added more sample programs, including an example of a Bare Bones translation of a two-symbol three-state Turing machine busy beaver program. The Bare Bones program demonstrates that Bare Bones can perform any action that can be performed by a Turing machine, proving that Bare Bones is universal.
Brookshear states that Bare Bones is universal by comparing it to a Turing machine, but only actually shows that it can compute two simple functions that are computable by a Turing machine. He states that “researchers have shown that the Bare Bones programming language can be used to express algorithms for computing all the Turing-computable functions,” but unfortunately he does not give any reference to publication of such results.
One way to show that a machine or language is universal is to show that it can emulate a Turing machine, or that any Turing machine program can be translated into a program for the machine or language in question. At first glance it wasn’t obvious that Bare Bones could be universal, because a Bare Bones program can only use a finite number of variables. The key to its universality is that a Bare Bones variable can contain any non-negative integer. An infinitely long one-dimensional Turing machine tape can be represented as three variables: one contains all the symbols to the left of the head, one contains the symbol at the head, and one contains all the symbols to the right of the head. For a turing machine with an alphabet of n symbols, to move the head position right, one must execute these assignments (in pseudo-code, not Bare Bones code):
LEFT := LEFT * n + HEAD;HEAD := RIGHT mod n;RIGHT := RIGHT / n;
Moving the head position left is done by the same assignments, with LEFT and RIGHT swapped. For a two-symbol Turing machine, the Bare Bones code to move the head position to the right is:
# LEFT := LEFT * 2; copy LEFT to TEMP; while TEMP not 0 do; Â incr LEFT; Â decr TEMP; end;# LEFT := LEFT + HEAD; while HEAD not 0 do; Â incr LEFT; Â decr HEAD; end;# HEAD := RIGHT mod 2; # RIGHT := RIGHT / 2; copy RIGHT to TEMP; clear RIGHT; copy TEMP to HEAD; decr TEMP; while TEMP not 0 do; Â incr RIGHT; Â decr TEMP; Â copy TEMP to HEAD; Â decr TEMP; end;
That’s the most complicated portion of the Turing machine interpreter. I might write up an explanation of the rest at some future date. For now, anyone interested in the details can look at the example Bare Bones source code for a simulation of a three-state Turing Machine busy beaver program: tm_busy_beaver_3.bb.
Note that it is not currently practical to to use my Bare Bones interpreter to simulate a Turing machine with a long tape, because the division/modulus code used to shift the tape takes O(n) operations, where n is the number of bits to the right of (or left of) the head. To make that reasonably efficient, I’ll need to extend the interpreter’s optimizer, and make the interpreter use bignum arithmetic (probably using the GMP library). Once that’s done, it would be nice to try running a universal Turing machine in Bare Bones. The smallest two-symbol UTM I’ve heard of uses 22 states.