123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- #!/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 =~ /^0d/);
- }
- # 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";
|