How to resolve the algorithm Compiler/virtual machine interpreter step by step in the Raku programming language
How to resolve the algorithm Compiler/virtual machine interpreter step by step in the Raku programming language
Table of Contents
Problem Statement
A virtual machine implements a computer in software. Write a virtual machine interpreter. This interpreter should be able to run virtual assembly language programs created via the task. This is a byte-coded, 32-bit word stack based virtual machine. The program should read input from a file and/or stdin, and write output to a file and/or stdout. Input format: Given the following program: The output from the Code generator is a virtual assembly code program: The first line of the input specifies the datasize required and the number of constant strings, in the order that they are reference via the code. The data can be stored in a separate array, or the data can be stored at the beginning of the stack. Data is addressed starting at 0. If there are 3 variables, the 3rd one if referenced at address 2. If there are one or more constant strings, they come next. The code refers to these strings by their index. The index starts at 0. So if there are 3 strings, and the code wants to reference the 3rd string, 2 will be used. Next comes the actual virtual assembly code. The first number is the code address of that instruction. After that is the instruction mnemonic, followed by optional operands, depending on the instruction. Registers: sp: pc: Data: Instructions: Each instruction is one byte. The following instructions also have a 32-bit integer operand: where index is an index into the data array. where index is an index into the data array. where value is a 32-bit integer that will be pushed onto the stack. where (n) is a 32-bit integer specifying the distance between the current location and the desired location. addr is an unsigned value of the actual code address. where (n) is a 32-bit integer specifying the distance between the current location and the desired location. addr is an unsigned value of the actual code address. The following instructions do not have an operand. They perform their operation directly against the stack: For the following instructions, the operation is performed against the top two entries in the stack: For the following instructions, the operation is performed against the top entry in the stack: Print the word at stack top as a character. Print the word at stack top as an integer. Stack top points to an index into the string pool. Print that entry. Unconditional stop. Your solution should pass all the test cases above and the additional tests found Here. The C and Python versions can be considered reference implementations.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Compiler/virtual machine interpreter step by step in the Raku programming language
Source code in the raku programming language
my @CODE = q:to/END/.lines;
Datasize: 3 Strings: 2
"count is: "
"\n"
0 push 1
5 store [0]
10 fetch [0]
15 push 10
20 lt
21 jz (68) 65 # jump value adjusted
26 push 0
31 prts
32 fetch [0]
37 prti
38 push 1
43 prts
44 fetch [0]
49 push 1
54 add
55 store [0]
60 jmp (-87) 10 # jump value adjusted
65 halt
END
my (@stack, @strings, @data, $memory);
my $pc = 0;
(@CODE.shift) ~~ /'Datasize:' \s+ (\d+) \s+ 'Strings:' \s+ (\d+)/ or die "bad header";
my $w = $0; # 'wordsize' of op-codes and 'width' of data values
@strings.push: (my $s = @CODE.shift) eq '"\n"' ?? "\n" !! $s.subst(/'"'/, '', :g) for 1..$1;
sub value { substr($memory, ($pc += $w) - $w, $w).trim }
my %ops = (
'no-op' => sub { },
'add' => sub { @stack[*-2] += @stack.pop },
'sub' => sub { @stack[*-2] -= @stack.pop },
'mul' => sub { @stack[*-2] *= @stack.pop },
'div' => sub { @stack[*-2] /= @stack.pop },
'mod' => sub { @stack[*-2] %= @stack.pop },
'neg' => sub { @stack[*-1] = - @stack[*-1] },
'and' => sub { @stack[*-2] &&= @stack[*-1]; @stack.pop },
'or' => sub { @stack[*-2] ||= @stack[*-1]; @stack.pop },
'not' => sub { @stack[*-1] = @stack[*-1] ?? 0 !! 1 },
'lt' => sub { @stack[*-1] = @stack[*-2] < @stack.pop ?? 1 !! 0 },
'gt' => sub { @stack[*-1] = @stack[*-2] > @stack.pop ?? 1 !! 0 },
'le' => sub { @stack[*-1] = @stack[*-2] <= @stack.pop ?? 1 !! 0 },
'ge' => sub { @stack[*-1] = @stack[*-2] >= @stack.pop ?? 1 !! 0 },
'ne' => sub { @stack[*-1] = @stack[*-2] != @stack.pop ?? 1 !! 0 },
'eq' => sub { @stack[*-1] = @stack[*-2] == @stack.pop ?? 1 !! 0 },
'store' => sub { @data[&value] = @stack.pop },
'fetch' => sub { @stack.push: @data[&value] // 0 },
'push' => sub { @stack.push: value() },
'jmp' => sub { $pc += value() - $w },
'jz' => sub { $pc += @stack.pop ?? $w !! value() - $w },
'prts' => sub { print @strings[@stack.pop] },
'prti' => sub { print @stack.pop },
'prtc' => sub { print chr @stack.pop },
'halt' => sub { exit }
);
my %op2n = %ops.keys.sort Z=> 0..*;
my %n2op = %op2n.invert;
%n2op{''} = 'no-op';
for @CODE -> $_ {
next unless /\w/;
/^ \s* \d+ \s+ (\w+)/ or die "bad line $_";
$memory ~= %op2n{$0}.fmt("%{$w}d");
/'(' ('-'?\d+) ')' | (\d+) ']'? $/;
$memory ~= $0 ?? $0.fmt("%{$w}d") !! ' ' x $w;
}
loop {
my $opcode = substr($memory, $pc, $w).trim;
$pc += $w;
%ops{%n2op{ $opcode }}();
}
You may also check:How to resolve the algorithm Brazilian numbers step by step in the Delphi programming language
You may also check:How to resolve the algorithm Ethiopian multiplication step by step in the Racket programming language
You may also check:How to resolve the algorithm Taxicab numbers step by step in the DCL programming language
You may also check:How to resolve the algorithm Operator precedence step by step in the Julia programming language
You may also check:How to resolve the algorithm Read a specific line from a file step by step in the R programming language