// Path: src/main/java/uk/co/skyem/projects/emuZ80/asm/AssemblerException.java
// public static class InvalidASMDirectiveException extends AssemblerException {
// 	public InvalidASMDirectiveException(String name, int lineNumber, String line) {
// 		super(lineNumber, line, "Found an invalid ASM directive \"" + name + "\", Specified greedy for ASM directive, maybe you are looking for a normal Instruction?");
// 	}
// }
// 
// Path: src/main/java/uk/co/skyem/projects/emuZ80/asm/AssemblerException.java
// public static class InvalidInstructionException extends AssemblerException {
// 	public InvalidInstructionException(String name, int lineNumber, String line) {
// 		super(lineNumber, line, "Found an invalid Instruction \"" + name + "\"");
// 	}
// }
// 
// Path: src/main/java/uk/co/skyem/projects/emuZ80/asm/Token.java
// abstract class Token {
// 
// 	public static abstract class Instruction extends Token {
// 
// 		public void create(Assembler assembler, Arguments arguments) {
// 
// 		}
// 
// 		public abstract void insert(Program program, int offset);
// 	}
// 
// 	public static class Label extends Token {
// 
// 		public int position;
// 		public final String name;
// 
// 		public Label(String name) {
// 			this.name = name;
// 		}
// 	}
// 
// 	public static abstract class ASMDirective extends Instruction {
// 
// 	}
// 
// 	public static abstract class CPUInstruction extends Instruction {
// 
// 		public int position;
// 
// 		public void insertLabel(Assembler assembler, Label label) {
// 		}
// 	}
// 
// 	public static class LD extends CPUInstruction {
// 
// 		@Override
// 		public void create(Assembler assembler, Arguments arguments) {
// 
// 		}
// 
// 		@Override
// 		public void insert(Program program, int offset) {
// 
// 		}
// 	}
// 
// 	public static class NOP extends CPUInstruction {
// 
// 		@Override
// 		public void insert(Program program, int offset) {
// 			program.appendByte((byte) 0);
// 		}
// 	}
// 
// 	// TODO: get this working.
// 	/*public static class ORG extends ASMDirective {
// 
// 	}*/
// }
// 
// Path: src/main/java/uk/co/skyem/projects/emuZ80/asm/Token.java
// public static abstract class Instruction extends Token {
// 
// 	public void create(Assembler assembler, Arguments arguments) {
// 
// 	}
// 
// 	public abstract void insert(Program program, int offset);
// }
// 
// Path: src/main/java/uk/co/skyem/projects/emuZ80/util/InternalException.java
// public class InternalException extends RuntimeException {
// 	public InternalException() {
// 		super("Internal error! Please report everything to skyem123 because its all his fault.\nSorry, there is no more info than I can give...");
// 	}
// 
// 	public InternalException(Exception originalException) {
// 		super("Internal error! Please report everything to skyem123 because its all his fault.", originalException);
// 	}
// }


import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import uk.co.skyem.projects.emuZ80.asm.AssemblerException.InvalidASMDirectiveException;
import uk.co.skyem.projects.emuZ80.asm.AssemblerException.InvalidInstructionException;
import uk.co.skyem.projects.emuZ80.asm.Token.*;
import uk.co.skyem.projects.emuZ80.asm.Token.Instruction;
import uk.co.skyem.projects.emuZ80.util.InternalException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import org.apache.commons.collections4.map.MultiKeyMap;
	private List<Pair<CPUInstruction, Label>> labelsToInsert = new ArrayList<>();

	public Assembler(String source) {
		this.source = source;
	}

	private static void registerASMDirective(String name, Supplier<ASMDirective> supplier) {
		asmDirectives.put(name, supplier);
		registerInstruction(name, supplier::get);
	}

	private static void registerInstruction(String name, Supplier<Instruction> supplier) {
		instructions.put(name, supplier);
	}

	private Instruction getInstruction(String name, String arguments) throws InvalidInstructionException {
		name = name.toUpperCase();
		if (instructions.containsKey(name)) {
			try {
				Instruction instruction = instructions.get(name).get();
				instruction.create(this, Arguments.parse(arguments, lineNumber));
				return instruction;
			} catch (Exception e) {
				throw new InternalException(e);
			}
		} else {
			throw new InvalidInstructionException(name, lineNumber, line);
		}
	}

// Path: src/main/java/uk/co/skyem/projects/emuZ80/asm/AssemblerException.java
// public static class InvalidASMDirectiveException extends AssemblerException {
// 	public InvalidASMDirectiveException(String name, int lineNumber, String line) {
// 		super(lineNumber, line, "Found an invalid ASM directive \"" + name + "\", Specified greedy for ASM directive, maybe you are looking for a normal Instruction?");
// 	}
// }
// 
// Path: src/main/java/uk/co/skyem/projects/emuZ80/asm/AssemblerException.java
// public static class InvalidInstructionException extends AssemblerException {
// 	public InvalidInstructionException(String name, int lineNumber, String line) {
// 		super(lineNumber, line, "Found an invalid Instruction \"" + name + "\"");
// 	}
// }
// 
// Path: src/main/java/uk/co/skyem/projects/emuZ80/asm/Token.java
// abstract class Token {
// 
// 	public static abstract class Instruction extends Token {
// 
// 		public void create(Assembler assembler, Arguments arguments) {
// 
// 		}
// 
// 		public abstract void insert(Program program, int offset);
// 	}
// 
// 	public static class Label extends Token {
// 
// 		public int position;
// 		public final String name;
// 
// 		public Label(String name) {
// 			this.name = name;
// 		}
// 	}
// 
// 	public static abstract class ASMDirective extends Instruction {
// 
// 	}
// 
// 	public static abstract class CPUInstruction extends Instruction {
// 
// 		public int position;
// 
// 		public void insertLabel(Assembler assembler, Label label) {
// 		}
// 	}
// 
// 	public static class LD extends CPUInstruction {
// 
// 		@Override
// 		public void create(Assembler assembler, Arguments arguments) {
// 
// 		}
// 
// 		@Override
// 		public void insert(Program program, int offset) {
// 
// 		}
// 	}
// 
// 	public static class NOP extends CPUInstruction {
// 
// 		@Override
// 		public void insert(Program program, int offset) {
// 			program.appendByte((byte) 0);
// 		}
// 	}
// 
// 	// TODO: get this working.
// 	/*public static class ORG extends ASMDirective {
// 
// 	}*/
// }
// 
// Path: src/main/java/uk/co/skyem/projects/emuZ80/asm/Token.java
// public static abstract class Instruction extends Token {
// 
// 	public void create(Assembler assembler, Arguments arguments) {
// 
// 	}
// 
// 	public abstract void insert(Program program, int offset);
// }
// 
// Path: src/main/java/uk/co/skyem/projects/emuZ80/util/InternalException.java
// public class InternalException extends RuntimeException {
// 	public InternalException() {
// 		super("Internal error! Please report everything to skyem123 because its all his fault.\nSorry, there is no more info than I can give...");
// 	}
// 
// 	public InternalException(Exception originalException) {
// 		super("Internal error! Please report everything to skyem123 because its all his fault.", originalException);
// 	}
// }


// Path: src/main/java/uk/co/skyem/projects/emuZ80/asm/Assembler.java
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import uk.co.skyem.projects.emuZ80.asm.AssemblerException.InvalidASMDirectiveException;
import uk.co.skyem.projects.emuZ80.asm.AssemblerException.InvalidInstructionException;
import uk.co.skyem.projects.emuZ80.asm.Token.*;
import uk.co.skyem.projects.emuZ80.asm.Token.Instruction;
import uk.co.skyem.projects.emuZ80.util.InternalException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import org.apache.commons.collections4.map.MultiKeyMap;

	private List<Pair<CPUInstruction, Label>> labelsToInsert = new ArrayList<>();

	public Assembler(String source) {
		this.source = source;
	}

	private static void registerASMDirective(String name, Supplier<ASMDirective> supplier) {
		asmDirectives.put(name, supplier);
		registerInstruction(name, supplier::get);
	}

	private static void registerInstruction(String name, Supplier<Instruction> supplier) {
		instructions.put(name, supplier);
	}

	private Instruction getInstruction(String name, String arguments) throws InvalidInstructionException {
		name = name.toUpperCase();
		if (instructions.containsKey(name)) {
			try {
				Instruction instruction = instructions.get(name).get();
				instruction.create(this, Arguments.parse(arguments, lineNumber));
				return instruction;
			} catch (Exception e) {
				throw new InternalException(e);
			}
		} else {
			throw new InvalidInstructionException(name, lineNumber, line);
		}
	}

	private ASMDirective getASMDirective(String name, String arguments) throws InvalidASMDirectiveException {