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();
}