use v6; use PDF::Syntax; class PDF::Grammar::Actions { method Str() { return ~self.WHAT; } method pdf_object($/) { my $val = $($); my $stream = $; if ($stream && ! $val.isa(PDF::Syntax::Dictionary)) { die 'Stream must be prefixed by a dictionary'; } make PDF::Syntax::Object.new(:objnum(int +$), :gennum(int +$), :val($val), :stream($stream)); } method pdf_dictionary($/) { my %val; # I can't just use "@($/)" because that always resolves to zero -- I don't know why # I need to treat @($/) as an actual list before it works properly for @($/).list -> $i { my $pair = $($i); my $key = $pair.key.label; %val{~$key} = $pair.value; } make PDF::Syntax::Dictionary.new(:val(%val)); } method pair($/) { my $key = $($); my $val = $($); make Pair.new(:key($key), :value($val)); } method pdf_array($/) { my $val = map { $($_) }, @($/).list; make PDF::Syntax::Array.new(:val($val)); } method pdf_name($/) { make PDF::Syntax::Name.new(:name($($))); } method pdf_reference($/) { make PDF::Syntax::Reference.new(:objnum(int +$), :gennum(int +$)); } method pdf_number($/) { make PDF::Syntax::Number.new(:val(+$/)); } method pdf_string($/) { make PDF::Syntax::String.new( :val($($)) ); } method pdf_hexstring($/) { make PDF::Syntax::Hexstring.new( :val($($)) ); } method pdf_boolean($/) { make $/ eq 'true' ?? $PDF::Syntax::Boolean::TRUE !! $PDF::Syntax::Boolean::FALSE; } method pdf_null($/) { make $PDF::Syntax::Null::NULL; } method pdf_any($/) { make $( $ ); } method name_label($/) { my @val = map { $($_) }, @($/).list; make @val.join(''); } method name_label_body($/, $key) { make $($/{$key}); } method name_label_escape($/) { my $in = ~$/; make chr($in.substr(1).to_int(16)); # hex "#nn" } method literal_string($/) { my @val = map { $($_) }, @($/).list; make @val.join(''); } method literal_string_body($/, $key) { if 'literal_string' eq $key { make '(' ~ $($/{$key}) ~ ')'; # restore literal balanced parens } else { make $($/{$key}); } } method literal_string_escape($/) { my Str $in = ~$/; # TODO: perhaps rewrite as a hash plus an exception my Str $char = $in eq '\\\\' ?? '\\' !! $in eq '\\(' ?? '(' !! $in eq '\\)' ?? ')' !! $in eq '\\n' ?? "\n" !! $in eq '\\r' ?? "\r" !! $in eq '\\t' ?? "\t" !! $in eq '\\f' ?? "\f" !! $in eq '\\b' ?? chr(8) !! chr($in.substr(1).to_int(8)); # octal "\nnn" #say "ESCAPE: in $in, char: '$char'"; make $char; } method hexstring($/) { my @val; my $digits = 0; for @($/).list -> $i { my Int $val = $($i); if 0 <= $val { # skip the "-1" tokens if 0 == $digits % 2 { @val.push($val +< 4); # high nybble } else { @val[*-1] += $val; # include low nybble, if any } ++$digits; } } make @val; } method hexchar_space($/, $key) { # The "-1" is a flag to ignore this token make 'hex' eq $key ?? $($/).to_int(16) !! -1; } }