Skip to content

Instantly share code, notes, and snippets.

@bennydictor
Created April 25, 2019 12:13
Show Gist options
  • Save bennydictor/e483cb5d0c3d5bb17a871effdce319fc to your computer and use it in GitHub Desktop.
Save bennydictor/e483cb5d0c3d5bb17a871effdce319fc to your computer and use it in GitHub Desktop.
BrainFuck interpreter/jit compiler using java's MethodHandle
package org.bennydict.brainmethodfuckhandles;
import java.io.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.Scanner;
public class BrainFuckMethodHandleCompiler {
public static void main(String[] args) {
String code = new Scanner(System.in).nextLine();
MethodHandle compiled = compile(code);
BrainFuckVM vm = new BrainFuckVM(System.in, System.out);
try {
compiled.invoke(vm);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
static class CompileResult {
MethodHandle result;
String rest;
public CompileResult(MethodHandle result, String rest) {
this.result = result;
this.rest = rest;
}
}
static MethodHandle compile(String code) {
CompileResult result = null;
try {
result = compileLoop(code);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.toString());
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.toString());
}
if (!result.rest.isEmpty()) {
throw new IllegalArgumentException("bad code");
}
return result.result;
}
static CompileResult compileLoop(String code) throws NoSuchMethodException, IllegalAccessException {
MethodType type = MethodType.methodType(void.class);
MethodHandle ret = MethodHandles.empty(type);
HashMap<Character, String> nameMap = new HashMap<>();
nameMap.put('>', "gt");
nameMap.put('<', "lt");
nameMap.put('+', "plus");
nameMap.put('-', "minus");
nameMap.put('.', "dot");
nameMap.put(',', "comma");
while (!(code.isEmpty() || code.charAt(0) == ']')) {
MethodHandle next;
switch (code.charAt(0)) {
case '>':
case '<':
case '+':
case '-':
case '.':
case ',':
next = MethodHandles.lookup().findVirtual(BrainFuckVM.class, nameMap.get(code.charAt(0)), type);
ret = MethodHandles.doWhileLoop(ret, next, MethodHandles.constant(boolean.class, false));
break;
case '[':
CompileResult result = compileLoop(code.substring(1));
ret = MethodHandles.doWhileLoop(ret,
MethodHandles.whileLoop(null,
MethodHandles.lookup().findVirtual(BrainFuckVM.class, "testNZ", MethodType.methodType(boolean.class)),
result.result), MethodHandles.constant(boolean.class, false));
code = result.rest;
if (code.charAt(0) != ']') {
throw new IllegalArgumentException("bad code");
}
break;
case ']':
break;
}
code = code.substring(1);
}
return new CompileResult(ret, code);
}
}
class BrainFuckVM {
private InputStream in;
private PrintStream out;
public BrainFuckVM(InputStream in, PrintStream out) {
this.in = in;
this.out = out;
}
private byte[] tape = new byte[30000];
private int ptr = 0;
void gt() {
ptr += 1;
}
void lt() {
ptr -= 1;
}
void plus() {
tape[ptr] += 1;
}
void minus() {
tape[ptr] -= 1;
}
void dot() {
out.print((char) tape[ptr]);
}
void comma() {
try {
tape[ptr] = (byte) in.read();
} catch (IOException e) {
throw new IOError(e);
}
}
boolean testNZ() {
return tape[ptr] != 0;
}
}
@ManWhoLaughs
Copy link

BRILLIANT <3

@Kurt212
Copy link

Kurt212 commented May 13, 2021

GENIUS :3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment