use v5.16;
-chomp(my $packet = <>);
-$packet =~ s/./sprintf("%04b", hex $&)/ge if $packet =~ /[2-9A-F]/;
+chomp(my $data = <>);
+# Accept also a binary string instead of hexadecimal one:
+$data =~ s/./sprintf("%04b", hex $&)/ge if $data =~ /[2-9A-F]/;
-sub get_b (\$$) {
- my ($ppack, $bits) = @_;
+sub chop_bits {
+ my ($bits) = @_;
my $rv;
- $$ppack =~ s/.{$bits}/$rv=eval"0b$&";''/e;
- # say "get_b $bits=$rv";
+ $data =~ s/.{$bits}/$rv = eval "0b$&"; ''/e;
+ # say "chop_bits($bits) = $rv";
return $rv;
}
my $ver_sum;
my $result;
-sub parse {
- my $pp = shift;
- my $l = length $pp;
- my $ver = get_b($pp, 3);
- $ver_sum += $ver;
- my $typ = get_b($pp, 3);
+sub parse {
+ $ver_sum += chop_bits(3);
+ my $type = chop_bits(3);
- if ($typ == 4) {
+ if ($type == 4) {
my $num = 0;
- while (get_b($pp, 1)) {
+ my $more;
+ do {
+ $more = chop_bits(1);
$num *= 16;
- $num += get_b($pp, 4);
- }
- $num *= 16;
- $num += get_b($pp, 4);
+ $num += chop_bits(4);
+ } while ($more);
$result .= "$num,";
} else {
- $result .= "op($typ,";
- my $li = get_b($pp, 1);
- if ($li) {
- my $subp = get_b($pp, 11);
- for (1 .. $subp) {
- my $l1 = parse($pp);
- $pp =~ s/.{$l1}//;
- }
+ $result .= "op$type(";
+ if (chop_bits(1)) {
+ my $subparts = chop_bits(11);
+ parse() for 1 .. $subparts;
} else {
- my $subl = get_b($pp, 15);
- my $s = substr($pp, 0, $subl);
- $pp =~ s/.{$subl}//;
- while ($subl) {
- my $l1 = parse($s);
- $s =~ s/.{$l1}//;
- $subl -= $l1;
- }
+ my $sublen = chop_bits(15);
+ my $len = length $data;
+ parse() while $len - length($data) < $sublen;
}
$result .= "),";
}
- return $l - length($pp);
}
use List::Util qw(sum product min max);
-sub op {
- my ($id, @rest) = @_;
- if ($id == 0) {
- return sum @rest;
- } elsif ($id == 1) {
- return product @rest;
- } elsif ($id == 2) {
- return min @rest;
- } elsif ($id == 3) {
- return max @rest;
- } elsif ($id == 5) {
- return $rest[0] > $rest[1] ? 1 : 0;
- } elsif ($id == 6) {
- return $rest[0] < $rest[1] ? 1 : 0;
- } elsif ($id == 7) {
- return $rest[0] == $rest[1] ? 1 : 0;
- }
-}
+sub op0 { sum @_; }
+sub op1 { product @_; }
+sub op2 { min @_; }
+sub op3 { max @_; }
+sub op5 { $_[0] > $_[1] ? 1 : 0 }
+sub op6 { $_[0] < $_[1] ? 1 : 0 }
+sub op7 { $_[0] == $_[1] ? 1 : 0 }
+
+parse();
-parse($packet);
+say "versions = $ver_sum";
say $result, ' = ', eval $result;