78 lines
2.5 KiB
Raku
78 lines
2.5 KiB
Raku
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 }}();
|
|
}
|