yaict.pl 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #!/bin/perl
  2. use warnings;
  3. use strict;
  4. use Data::Dumper;
  5. my $VERSION = 0.01;
  6. # The array of intcodes making up the program
  7. my @program;
  8. # A hash of labels and their corresponding starting line number
  9. my %labels;
  10. # Opcodes that may be encounted and their corresponding ints
  11. my %opcodes = (
  12. "add" => "01",
  13. "mult" => "02",
  14. "in" => "03",
  15. "out" => "04",
  16. "jpt" => "05",
  17. "jpf" => "06",
  18. "lt" => "07",
  19. "eq" => "08",
  20. "mrb" => "09",
  21. "hlt" => "99",
  22. );
  23. # Additional instructions that don't correspond to opcodes but need to be processed
  24. # my %instructions = (
  25. # # Load
  26. # "ld" => \&load,
  27. # );
  28. # Load a value into a memory address
  29. sub load
  30. {
  31. my $loadAddress = shift;
  32. my $valueToLoad = shift;
  33. $program[$loadAddress] = $valueToLoad;
  34. }
  35. # Parse the assembly to IntCode
  36. sub parseAssembly
  37. {
  38. my @assembly = @_;
  39. # The current instruction number
  40. my $instructionNumber = 0;
  41. foreach my $line (@assembly)
  42. {
  43. $line =~ /^(\w+?:?)(?: (.+))?$/;
  44. my $asmInstruction = $1;
  45. my @parameters = split(/,\s?/, $2 || "");
  46. # Store labels by the address of their next instruction
  47. if($asmInstruction =~ /:$/)
  48. {
  49. $labels{substr($asmInstruction, 0, -1)} = ++$instructionNumber;
  50. next;
  51. }
  52. if($asmInstruction eq "ld")
  53. {
  54. my $storeAt = getAddress(shift @parameters);
  55. my $value = shift @parameters;
  56. load($storeAt, $value);
  57. }
  58. elsif($opcodes{$asmInstruction})
  59. {
  60. parseOpcodeAndParams($opcodes{$asmInstruction}, \@parameters, \$instructionNumber);
  61. }
  62. else
  63. {
  64. die "Failed to find matching ASM instruction for: $asmInstruction";
  65. }
  66. }
  67. }
  68. sub parseOpcodeAndParams
  69. {
  70. # A reference to the current instruction number
  71. # Need to assign first due to usage for opcode index
  72. my $instructionNumber = $_[2];
  73. # The opcode
  74. my $opcode = $_[0];
  75. # Store program index of opcode now because opcode may be modified in the parameter loop
  76. my $opcodeIdx = $$instructionNumber++;
  77. # Parameters for the opcode
  78. my @parameters = @{ $_[1] };
  79. # Store at is usually the last parameter, if there are any parameters
  80. my $storeAt = $#parameters > 0 ? getAddress(pop(@parameters)) : -1;
  81. # The amount of digits the opcode is expected to be when the parameter tries to add its parameter mode
  82. my $parameterModeIdx = 2;
  83. foreach my $param (@parameters)
  84. {
  85. if(isImmediate($param))
  86. {
  87. if(length($opcode) < $parameterModeIdx)
  88. {
  89. $opcode = "0$opcode";
  90. }
  91. $opcode = "1$opcode";
  92. }
  93. else
  94. {
  95. $param = getAddress($param);
  96. }
  97. $parameterModeIdx++;
  98. $program[$$instructionNumber++] = $param;
  99. if($$instructionNumber == 9)
  100. {
  101. print "$opcode, $param";
  102. die "Overwriting memory!";
  103. }
  104. }
  105. # Insert opcode back where it belongs
  106. $program[$opcodeIdx] = $opcode;
  107. if($storeAt > -1)
  108. {
  109. # Add the storeAt address
  110. $program[$$instructionNumber++] = $storeAt;
  111. }
  112. }
  113. # Check if a parameter is Immediate Mode or Position
  114. #
  115. # Parameter is considered Position mode if it's prefixed with "0d"
  116. sub isImmediate
  117. {
  118. return shift =~ /^-?\d+$/;
  119. }
  120. # Check if a parameter
  121. sub isLabel
  122. {
  123. $_[0] =~ /(\w+?)(?:\s*+\s*\d*)?/;
  124. print "Testing: $_[0]\n";
  125. return !!$labels{$1};
  126. }
  127. # Get the address a value represents
  128. sub getAddress
  129. {
  130. return (shift =~ s/^0d//r);
  131. }
  132. sub readFile
  133. {
  134. my @assembly;
  135. my $filePath = shift;
  136. open(my $fh, "<", $filePath) or die "Failed to open file";
  137. while(my $line = <$fh>)
  138. {
  139. chomp $line;
  140. # Remove comments from the line
  141. $line =~ s/\/\/.+$//;
  142. # Remove trailing whitespace
  143. $line =~ s/\s+$//;
  144. if ($line ne "") {
  145. push(@assembly, $line);
  146. }
  147. }
  148. return @assembly;
  149. }
  150. parseAssembly(readFile($ARGV[0]));
  151. print "[".join(",", @program)."]\n";