#!/bin/perl use warnings; use strict; use Data::Dumper; my $VERSION = 0.01; # The array of intcodes making up the program my @program; # A hash of labels and their corresponding starting line number my %labels; # Opcodes that may be encounted and their corresponding ints my %opcodes = ( "add" => "01", "mult" => "02", "in" => "03", "out" => "04", "jpt" => "05", "jpf" => "06", "lt" => "07", "eq" => "08", "mrb" => "09", "hlt" => "99", ); # Additional instructions that don't correspond to opcodes but need to be processed # my %instructions = ( # # Load # "ld" => \&load, # ); # Load a value into a memory address sub load { my $loadAddress = shift; my $valueToLoad = shift; $program[$loadAddress] = $valueToLoad; } # Parse the assembly to IntCode sub parseAssembly { my @assembly = @_; # The current instruction number my $instructionNumber = 0; foreach my $line (@assembly) { $line =~ /^(\w+?:?)(?: (.+))?$/; my $asmInstruction = $1; my @parameters = split(/,\s?/, $2 || ""); # Store labels by the address of their next instruction if($asmInstruction =~ /:$/) { $labels{substr($asmInstruction, 0, -1)} = ++$instructionNumber; next; } if($asmInstruction eq "ld") { my $storeAt = getAddress(shift @parameters); my $value = shift @parameters; load($storeAt, $value); } elsif($opcodes{$asmInstruction}) { parseOpcodeAndParams($opcodes{$asmInstruction}, \@parameters, \$instructionNumber); } else { die "Failed to find matching ASM instruction for: $asmInstruction"; } } } sub parseOpcodeAndParams { # A reference to the current instruction number # Need to assign first due to usage for opcode index my $instructionNumber = $_[2]; # The opcode my $opcode = $_[0]; # Store program index of opcode now because opcode may be modified in the parameter loop my $opcodeIdx = $$instructionNumber++; # Parameters for the opcode my @parameters = @{ $_[1] }; # Store at is usually the last parameter, if there are any parameters my $storeAt = $#parameters > 0 ? getAddress(pop(@parameters)) : -1; # The amount of digits the opcode is expected to be when the parameter tries to add its parameter mode my $parameterModeIdx = 2; foreach my $param (@parameters) { if(isImmediate($param)) { if(length($opcode) < $parameterModeIdx) { $opcode = "0$opcode"; } $opcode = "1$opcode"; } else { $param = getAddress($param); } $parameterModeIdx++; $program[$$instructionNumber++] = $param; if($$instructionNumber == 9) { print "$opcode, $param"; die "Overwriting memory!"; } } # Insert opcode back where it belongs $program[$opcodeIdx] = $opcode; if($storeAt > -1) { # Add the storeAt address $program[$$instructionNumber++] = $storeAt; } } # Check if a parameter is Immediate Mode or Position # # Parameter is considered Position mode if it's prefixed with "0d" sub isImmediate { return shift =~ /^-?\d+$/; } # Check if a parameter sub isLabel { $_[0] =~ /(\w+?)(?:\s*+\s*\d*)?/; print "Testing: $_[0]\n"; return !!$labels{$1}; } # Get the address a value represents sub getAddress { return (shift =~ s/^0d//r); } sub readFile { my @assembly; my $filePath = shift; open(my $fh, "<", $filePath) or die "Failed to open file"; while(my $line = <$fh>) { chomp $line; # Remove comments from the line $line =~ s/\/\/.+$//; # Remove trailing whitespace $line =~ s/\s+$//; if ($line ne "") { push(@assembly, $line); } } return @assembly; } parseAssembly(readFile($ARGV[0])); print "[".join(",", @program)."]\n";