Assembler { public static List<Instruction> processDollarLabelOperands(List<Instruction> instructions) { List<Instruction> processedAssembly = new ArrayList<>(); long nextLabelIndex = 0; List<Pair<Long, String>> labelsToInsert = new ArrayList<>(); long instructionNumber = 0; long lastOriginalLine = 0; for (Instruction instruction : instructions) { lastOriginalLine = instruction.originalLine; if (!((instruction.type == InstructionType.INSTRUCTION && instruction.opcode == null) || (instruction.type == InstructionType.DIRECTIVE && instruction.directive == null))) { instructionNumber++; } List<Pair<Long, String>> labelsLeftToInsert = new ArrayList<>(); for (Pair<Long, String> labelToInsert : labelsToInsert) { if (labelToInsert.a.equals(instructionNumber)) { processedAssembly.add(new Instruction(InstructionType.INSTRUCTION, labelToInsert.b, null, null, null, null, null, instruction.originalLine)); } else { labelsLeftToInsert.add(labelToInsert); } } labelsToInsert = labelsLeftToInsert; if (instruction.labelOperand != null) { Matcher dollarMatcher = dollarPattern.matcher(instruction.labelOperand); Instruction substitutedInstruction = instruction; while (dollarMatcher.find()) { nextLabelIndex++; if (dollarMatcher.group("current") != null) { processedAssembly.add(new Instruction(InstructionType.INSTRUCTION, "__dollar" + nextLabelIndex, null, null, null, null, null, instruction.originalLine)); substitutedInstruction = new Instruction( substitutedInstruction.type, substitutedInstruction.label, substitutedInstruction.opcode, substitutedInstruction.constantOperand, substitutedInstruction.labelOperand.replace("$C", "__dollar" + nextLabelIndex), substitutedInstruction.comment, substitutedInstruction.directive, substitutedInstruction.originalLine); } else if (dollarMatcher.group("number") != null) { Long maybeNumber = tryParse(dollarMatcher.group("number")); if (maybeNumber != null) { labelsToInsert.add(new Pair<>(instructionNumber + 1 + maybeNumber, "__dollar" + nextLabelIndex)); substitutedInstruction = new Instruction( substitutedInstruction.type, substitutedInstruction.label, substitutedInstruction.opcode, substitutedInstruction.constantOperand, substitutedInstruction.labelOperand.replace("$" + maybeNumber, "__dollar" + nextLabelIndex), substitutedInstruction.comment, substitutedInstruction.directive, substitutedInstruction.originalLine); } else { throw new IllegalArgumentException("Label operand '$" + dollarMatcher.group("number") + "' could not be parsed"); } } else { throw new IllegalArgumentException("Cannot process dollar label '" + dollarMatcher.group(0) + "'"); } dollarMatcher = dollarPattern.matcher(substitutedInstruction.labelOperand); } processedAssembly.add(substitutedInstruction); } else { processedAssembly.add(instruction); } } instructionNumber++; List<Pair<Long, String>> labelsLeftToInsert = new ArrayList<>(); for (Pair<Long, String> labelToInsert : labelsToInsert) { if (labelToInsert.a.equals(instructionNumber)) { processedAssembly.add(new Instruction(InstructionType.INSTRUCTION, labelToInsert.b, null, null, null, null, null, lastOriginalLine)); } else { labelsLeftToInsert.add(labelToInsert); } } if (labelsLeftToInsert.size() > 0) { throw new ArrayIndexOutOfBoundsException("A dollar label wants to be inserted beyond the end of the code."); } return processedAssembly; } private  Assembler(); @Nullable static AssemblerConfig loadConfig(@Nullable Reader configReader); static List<String> assemble(@Nonnull String input, @Nullable AssemblerConfig config, @Nonnull PrintStream logger, boolean insertIOConfiguration); @Nonnull static List<Instruction> parseAssembly(@Nonnull String input); static List<Instruction> insertIOConfiguration(List<Instruction> instructions, AssemblerConfig config); static List<Instruction> processDollarLabelOperands(List<Instruction> instructions); static List<Instruction> processChanDirectives(List<Instruction> instructions, AssemblerConfig config); static List<Instruction> processDataDirectives(List<Instruction> instructions); static Assembly makeLabelMap(List<Instruction> instructions); static Map<String, Long> getConfigLabels(@Nonnull AssemblerConfig config); static Assembly patchLabels(Assembly assembly); static List<Byte> processIndirectInstruction(Instruction instruction); static List<Byte> processDirectInstructionWithLabelOperand(Instruction instruction, Map<String, Long> labelMap); static byte processDirectInstructionWithConstantOperand(Instruction instruction); static List<Instruction> prefix(Instruction instruction); static ParseTree parseExpression(String expression, Function<AssemblerExpressionParser, ParseTree> startRule); final static long maxInt; final static long minInt; final static Pattern inputComment; final static Pattern instructionPattern; final static Pattern directivePattern; final static Pattern dollarPattern; static Map<String, Opcode> opcodes; }
@Test public void testProcessDollarLabelOperandSimple() { List<Instruction> instructions = Arrays.asList( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "ALABEL-$1", null, null, 1), new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldc"), 5L, null, null, null, 2), new Instruction(InstructionType.INSTRUCTION, "ALABEL", Assembler.opcodes.get("ajw"), 1L, null, null, null, 3) ); List<Instruction> processedInstructions = Assembler.processDollarLabelOperands(instructions); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "ALABEL-__dollar1", null, null, 1), processedInstructions.get(0)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldc"), 5L, null, null, null, 2), processedInstructions.get(1)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "__dollar1", null, null, null, null, null, 3), processedInstructions.get(2)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "ALABEL", Assembler.opcodes.get("ajw"), 1L, null, null, null, 3), processedInstructions.get(3)); }
@Test public void testProcessMultipleDollarLabelOperand() { List<Instruction> instructions = Arrays.asList( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "$2-$1", null, null, 1), new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("j"), null, "$0-$C", null, null, 2), new Instruction(InstructionType.INSTRUCTION, "ALABEL", Assembler.opcodes.get("ajw"), 1L, null, null, null, 3) ); List<Instruction> processedInstructions = Assembler.processDollarLabelOperands(instructions); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "__dollar1-__dollar2", null, null, 1), processedInstructions.get(0)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "__dollar4", null, null, null, null, null, 2), processedInstructions.get(1)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("j"), null, "__dollar3-__dollar4", null, null, 2), processedInstructions.get(2)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "__dollar2", null, null, null, null, null, 3), processedInstructions.get(3)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "__dollar3", null, null, null, null, null, 3), processedInstructions.get(4)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "ALABEL", Assembler.opcodes.get("ajw"), 1L, null, null, null, 3), processedInstructions.get(5)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "__dollar1", null, null, null, null, null, 3), processedInstructions.get(6)); }
@Test public void testProcessDollarLabelOperandZero() { List<Instruction> instructions = Arrays.asList( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "ALABEL-$0", null, null, 1), new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldc"), 5L, null, null, null, 2), new Instruction(InstructionType.INSTRUCTION, "ALABEL", Assembler.opcodes.get("ajw"), 1L, null, null, null, 3) ); List<Instruction> processedInstructions = Assembler.processDollarLabelOperands(instructions); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "ALABEL-__dollar1", null, null, 1), processedInstructions.get(0)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "__dollar1", null, null, null, null, null, 2), processedInstructions.get(1)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldc"), 5L, null, null, null, 2), processedInstructions.get(2)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "ALABEL", Assembler.opcodes.get("ajw"), 1L, null, null, null, 3), processedInstructions.get(3)); }
@Test public void testProcessDollarLabelOperandCurrent() { List<Instruction> instructions = Arrays.asList( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "ALABEL-$C", null, null, 1), new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldc"), 5L, null, null, null, 2) ); List<Instruction> processedInstructions = Assembler.processDollarLabelOperands(instructions); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "__dollar1", null, null, null, null, null, 1), processedInstructions.get(0)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "ALABEL-__dollar1", null, null, 1), processedInstructions.get(1)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldc"), 5L, null, null, null, 2), processedInstructions.get(2)); }
@Test public void testProcessDollarLabelOperandWithInterspersedDirectiveAndLabel() { List<Instruction> instructions = Arrays.asList( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "ALABEL-$2", null, null, 1), new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldc"), 5L, null, null, null, 2), new Instruction(InstructionType.INSTRUCTION, "justalabel", null, null, null, "and comment", null, 3), new Instruction(InstructionType.DIRECTIVE, null, null, null, null, null, "#chan chan0", 4), new Instruction(InstructionType.INSTRUCTION, "ALABEL", Assembler.opcodes.get("ajw"), 1L, null, null, null, 5) ); List<Instruction> processedInstructions = Assembler.processDollarLabelOperands(instructions); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("j"), null, "ALABEL-__dollar1", null, null, 1), processedInstructions.get(0)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldc"), 5L, null, null, null, 2), processedInstructions.get(1)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "justalabel", null, null, null, "and comment", null, 3), processedInstructions.get(2)); checkInstructionEquality( new Instruction(InstructionType.DIRECTIVE, null, null, null, null, null, "#chan chan0", 4), processedInstructions.get(3)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "__dollar1", null, null, null, null, null, 5), processedInstructions.get(4)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "ALABEL", Assembler.opcodes.get("ajw"), 1L, null, null, null, 5), processedInstructions.get(5)); }