Skip to content

Instantly share code, notes, and snippets.

@eutro
Last active December 8, 2020 14:32
Show Gist options
  • Save eutro/2b63a8eb0d27f8e72b640469db38967b to your computer and use it in GitHub Desktop.
Save eutro/2b63a8eb0d27f8e72b640469db38967b to your computer and use it in GitHub Desktop.
Day 8 of AoC 2020 but it compiles to Java bytecode.
import java.io.*;
import java.util.*;
import java.util.stream.*;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.*;
import static org.objectweb.asm.Opcodes.*;
public class Day8 {
public static void main(String[] args) throws Exception {
Program.Interpreted program = Program.from(System.in);
System.out.println("Part 1:");
try {
System.out.println(program.compile().run());
} catch (Program.CyclicProgramException e) {
System.out.println(e.getMessage());
}
System.out.println("Part 2:");
for (Program.Instruction insn : program.instructions) {
if (!insn.flip()) continue;
try {
System.out.println(program.compile().run());
} catch (Program.CyclicProgramException e) {
insn.flip();
continue;
}
break;
}
}
public interface Program {
public static Interpreted from(InputStream stream) {
return from(new InputStreamReader(stream));
}
public static Interpreted from(Reader reader) {
return from(new BufferedReader(reader));
}
public static Interpreted from(BufferedReader bufferedReader) {
List<Instruction> instructions = bufferedReader
.lines()
.filter(s -> !s.isEmpty())
.map(Instruction::read)
.collect(Collectors.toList());
return from(instructions);
}
public static Interpreted from(List<Instruction> instructions) {
return new Interpreted(instructions);
}
class Interpreted implements Program {
public List<Instruction> instructions;
public Interpreted(List<Instruction> instructions) {
this.instructions = instructions;
}
@Override
public int run() throws CyclicProgramException {
boolean[] visited = new boolean[instructions.size()];
Instruction instruction;
int program = 0;
int accumulator = 0;
while (program < instructions.size()) {
if (visited[program]) {
throw new CyclicProgramException(accumulator);
}
visited[program] = true;
instruction = instructions.get(program);
program = instruction.advanceProgram(program);
accumulator = instruction.advanceAccumulator(accumulator);
}
return accumulator;
}
private static class Loader extends ClassLoader {
public Class<?> defineClass(String name, byte[] bytes) {
return defineClass(name, bytes, 0, bytes.length);
}
}
private static int names = 0;
@Override
public Program compile() {
String pClassName = Program.class
.getName()
.replace('.', '/');
String className = pClassName + "$Compiled" + names++;
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(V1_1,
ACC_PUBLIC,
className,
null,
"java/lang/Object",
new String[] { pClassName });
Method m = Method.getMethod("void <init> ()");
GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
mg.loadThis();
mg.invokeConstructor(Type.getType(Object.class), m);
mg.returnValue();
mg.endMethod();
m = Method.getMethod("int run ()");
mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
Label[] labels = new Label[instructions.size() + 1];
for (int i = 0; i <= instructions.size(); ++i) {
labels[i] = mg.newLabel();
}
int seen = mg.newLocal(Type.getType(boolean[].class));
mg.push(instructions.size());
mg.newArray(Type.BOOLEAN_TYPE);
mg.storeLocal(seen);
int accumulator = mg.newLocal(Type.getType(int.class));
mg.push(0);
mg.storeLocal(accumulator);
Label postThrow = mg.newLabel();
mg.goTo(postThrow);
Label doThrow = mg.mark();
mg.newInstance(Type.getType(CyclicProgramException.class));
mg.dup();
mg.loadLocal(accumulator);
mg.invokeConstructor(Type.getType(CyclicProgramException.class),
Method.getMethod("void <init>(int)"));
mg.throwException();
mg.mark(postThrow);
int i = 0;
for (Instruction insn : instructions) {
insn.compile(mg, seen, accumulator, labels, i, doThrow);
i++;
}
mg.mark(labels[labels.length - 1]);
mg.loadLocal(accumulator);
mg.returnValue();
mg.endMethod();
m = new Method("compile", "()L" + pClassName + ";");
mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
mg.loadThis();
mg.returnValue();
mg.endMethod();
m = Method.getMethod("String toString ()");
mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
mg.push("<compiled program>");
mg.returnValue();
mg.endMethod();
cw.visitEnd();
try {
return (Program) new Loader()
.defineClass(className, cw.toByteArray())
.getConstructor()
.newInstance();
} catch (Exception | Error e) {
e.printStackTrace();
return this;
}
}
@Override
public String toString() {
return instructions.toString();
}
}
int run() throws CyclicProgramException;
Program compile();
class CyclicProgramException extends Exception {
public final int accumulator;
public CyclicProgramException(int accumulator) {
super(String.format("Cyclic program. (Accumulator: %s)",
accumulator));
this.accumulator = accumulator;
}
}
public class Instruction {
public static enum Operation {
NOP,
JMP,
ACC,
;
public int advanceAccumulator(int accumulator, int value) {
return accumulator + (this == ACC ? value : 0);
}
public int advanceProgram(int program, int value) {
return program + (this == JMP ? value : 1);
}
public void compile(GeneratorAdapter mg, int accumulator,
Label[] labels, int index, int value) {
switch (this) {
case NOP:
break;
case JMP:
mg.goTo(labels[index + value >= labels.length ?
labels.length - 1 :
index + value]);
break;
case ACC:
mg.loadLocal(accumulator);
mg.push(value);
mg.math(GeneratorAdapter.ADD, Type.INT_TYPE);
mg.storeLocal(accumulator);
break;
}
}
public Operation flip() {
switch (this) {
case NOP:
return JMP;
case JMP:
return NOP;
default:
return this;
}
}
}
public Operation code;
public int value;
public Instruction(Operation code, int value) {
this.code = code;
this.value = value;
}
public static Instruction read(String line) {
return new Instruction(Operation.valueOf(line.substring(0, 3).toUpperCase()),
Integer.parseInt(line.substring(4)));
}
public int advanceAccumulator(int accumulator) {
return code.advanceAccumulator(accumulator, value);
}
public int advanceProgram(int program) {
return code.advanceProgram(program, value);
}
public boolean flip() {
return code != (code = code.flip());
}
public void compile(GeneratorAdapter mg, int seen, int accumulator,
Label[] labels, int index, Label doThrow) {
mg.mark(labels[index]);
mg.loadLocal(seen);
mg.push(index);
mg.arrayLoad(Type.BOOLEAN_TYPE);
mg.ifZCmp(GeneratorAdapter.NE, doThrow);
mg.loadLocal(seen);
mg.push(index);
mg.push(true);
mg.arrayStore(Type.BOOLEAN_TYPE);
code.compile(mg, accumulator, labels, index, value);
}
@Override
public String toString() {
return code.toString() + " " + value;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment