Javagony Turing-completeness proof
This page will show the Turing-completeness of Javagony by implementing a brainfuck interpreter in it. (This was before the author realized Java had the assert statement, which would've made things a lot easier.)
The code
Main.java
- The starting point for the program
- Creates a new Proof and calls its doWhileValueNe0 method
class Main { public static void main(String[] args) { Proof n = new Proof(); n.tape.write(48); n.doWhileReadNe0(n::F1); } }
Proof.java
- Class to use the Tape class and a doWhileValueNe0 method
- The real "meat" of the proof
class Proof { public Tape tape; public Proof() { this.tape = new Tape(); } public void doWhileReadNe0(VFuncV f) { //Loop until this.tape.read()==0 int[] a = new int[1]; try { System.out.print(a[this.tape.read()] + "\r"); } catch(IndexOutOfBoundsException e) { f.vfuncv(); this.doWhileReadNe0(f); } } public void F1() { System.out.println("Tape: " + this.tape.read()); this.tape.write(this.tape.read() - 1); } }
Tape.java
- Class to simulate brainfuck's tape
- Bounded cells, but unbounded length
import java.util.List; import java.util.ArrayList; class Tape { private List<Integer> tape; private int head; public Tape() { this.tape = new ArrayList<Integer>(); this.tape.add(0); this.head = 0; } public void left() { this.head -= 1; } public void right() { this.head += 1; try { System.out.print(this.tape.get(this.head) + "\r"); } catch(IndexOutOfBoundsException e) { this.tape.add(0); } } public void write(Integer i) { this.tape.set(this.head, i); } public int read() { return this.tape.get(this.head); } }
VFuncV.java
- SAM type that takes no arguments and returns no value
- Used in the Proof class's doWhileReadNe0 method to simulate a while loop
interface VFuncV { void vfuncv(); }
The proof
VFuncV.java contains an SAM type, VFuncV, that takes no arguments and returns no value. The Proof class's doWhileReadNe0 method takes a VFuncV as a parameter and repeatedly evaluates it until its tape's read() method returns 0; this is in effect a while loop. The way this and the tape are implemented, as well as the way it's called with the Proof class's F1 method, should make Turing-completeness clear. For every [while loop]
in a brainfuck program, add a new method to the Proof class, and make that method call the doWhileReadNe0 method again if brackets are nested.
Brainfuck | Javagony |
---|---|
+ |
tape.write(tape.read() + 1);
|
- |
tape.write(tape.read() - 1);
|
< |
tape.left();
|
> |
tape.right();
|
[ |
Add a new method to the Proof class |
] |
Add a new method to the Proof class |
IO is unnecessary (integers followed by \r (carriage return, to blank them out) are constantly being printed, so it would be difficult anyways without changing the method).
And now for a brainfuck interpreter...
This interprets a version of brainfuck with no IO and changed commands as follows:
Brainfuck | Brainfuck::Javagony |
---|---|
+ |
(
|
- |
)
|
> |
*
|
< |
+
|
[ |
,
|
] |
-
|
The interpreter is the interpret method of the Proof class. Main.java takes one command-line argument, the code to run.
Main.java
class Main { public static void main(String[] args) { try { Proof p = new Proof(); String program = args[0]; p.interpret(program); } catch(IndexOutOfBoundsException e) { System.out.println("Usage: Main.java code"); } } }
Proof.java
import java.util.Stack; import java.util.Map; import java.util.HashMap; class Proof { public Tape tape; public int i; //for repeatNTimes public String codestr; //for matchBrackets public int codeIndex; public Stack<Integer> brktmp; public Map<Integer, Integer> brkmap; public Proof() { this.tape = new Tape(); this.brktmp = new Stack<Integer>(); this.brkmap = new HashMap<Integer, Integer>(); } public void doWhileReadNe0(IVFuncV f) { int[] a = new int[1]; try { System.out.print(a[this.tape.read()] + "\r"); } catch(IndexOutOfBoundsException e) { f.vfuncv(); this.doWhileReadNe0(f); } } public void repeatNTimes(IVFuncV f, int n) { this.i = 0; this.repeatNTimesPri(f, n); } private void repeatNTimesPri(IVFuncV f, int n) { int[] a = new int[n]; try { System.out.print(a[this.i] + "\r"); f.vfuncv(); ++this.i; this.repeatNTimesPri(f, n); } catch(IndexOutOfBoundsException e) {} } public void interpret(String code) { this.codestr = code; this.matchBrackets(); this.codeIndex = 0; this.interpretPri(); } private void interpretPri() { try { System.out.print(this.codestr.charAt(this.codeIndex) + "\r"); this.execOneCharacter(); this.interpretPri(); } catch(StringIndexOutOfBoundsException e) {} } public void matchBrackets() { this.repeatNTimes(this::matchBracketsIter, this.codestr.length()); } public void matchBracketsIter() { int c = (int) this.codestr.charAt(this.i); int[] a = new int[1]; try { System.out.print(a[44 - c] + "\r"); //Handle [ this.brktmp.push(this.i); } catch(IndexOutOfBoundsException e1) { try { System.out.print(a[45 - c] + "\r"); //Handle ] int n = this.brktmp.pop(); this.brkmap.put(n, this.i); this.brkmap.put(this.i, n); } catch(IndexOutOfBoundsException e2) {} } } private void execOneCharacter() { int c = (int) this.codestr.charAt(this.codeIndex) - 40; //0..5 int[] a = new int[1]; try { System.out.print(a[5 - c] + "\r"); //Handle ] this.codeIndex = this.brkmap.get(this.codeIndex) - 1; } catch(IndexOutOfBoundsException e1) { try { System.out.print(a[4 - c] + "\r"); //Handle [ try { System.out.print(a[this.tape.read()]); this.codeIndex = this.brkmap.get(this.codeIndex); } catch(IndexOutOfBoundsException e1a) {} } catch(IndexOutOfBoundsException e2) { try { System.out.print(a[3 - c] + "\r"); //Handle < this.tape.left(); } catch(IndexOutOfBoundsException e3) { try { System.out.print(a[2 - c] + "\r"); //Handle > this.tape.right(); } catch(IndexOutOfBoundsException e4) { try { System.out.print(a[1 - c] + "\r"); //Handle - this.tape.write(this.tape.read() - 1); } catch(IndexOutOfBoundsException e5) { //Handle + this.tape.write(this.tape.read() + 1); } } } } } ++this.codeIndex; } }
Tape.java
import java.util.List; import java.util.ArrayList; class Tape { private List<Integer> tape; private int head; public Tape() { this.tape = new ArrayList<Integer>(); this.tape.add(0); this.head = 0; } public void left() { this.head -= 1; } public void right() { this.head += 1; try { System.out.print(this.tape.get(this.head) + "\r"); } catch(IndexOutOfBoundsException e) { this.tape.add(0); } } public void write(Integer i) { this.tape.set(this.head, i); } public int read() { return this.tape.get(this.head); } }
IVFuncV.java
interface IVFuncV { void vfuncv(); }