File Coverage

blib/lib/Pandoc/Walker.pm
Criterion Covered Total %
statement 83 84 98.8
branch 25 28 89.2
condition 19 27 70.3
subroutine 16 17 94.1
pod 4 4 100.0
total 147 160 91.8


line stmt bran cond sub pod time code
1             package Pandoc::Walker;
2 31     31   66777 use strict;
  31         76  
  31         870  
3 31     31   144 use warnings;
  31         65  
  31         733  
4 31     31   469 use 5.010;
  31         98  
5              
6             our $VERSION = '0.34';
7              
8 31     31   164 use Scalar::Util qw(reftype blessed);
  31         57  
  31         1283  
9 31     31   170 use Carp;
  31         58  
  31         1337  
10              
11 31     31   11265 use parent 'Exporter';
  31         7678  
  31         165  
12             our @EXPORT = qw(walk query transform);
13             our @EXPORT_OK = ( @EXPORT, 'action' );
14              
15             sub _simple_action {
16 4042   50 0   7982 my $action = shift // return sub { };
        4042      
17              
18 4042 100 66     17456 if ( blessed $action and $action->isa('Pandoc::Filter') ) {
    50 33        
19 2         8 $action = $action->action;
20             }
21             elsif ( !ref $action or ref $action ne 'CODE' ) {
22 0   0     0 croak "expected code reference, got: " . ( $action // 'undef' );
23             }
24              
25 4042 100       6864 if (@_) {
26 2098         4229 my @args = @_;
27 2098     1875   7139 return sub { local $_ = $_[0]; $action->( $_[0], @args ) };
  1875         2416  
  1875         2960  
28             }
29             else {
30 1944         3410 return $action;
31             }
32             }
33              
34             sub action {
35 4024     4024 1 6413 my @actions;
36             my @args;
37              
38             # $selector => $action [, @arguments ]
39 4024 100       8820 if ( !ref $_[0] ) {
    100          
40 7   100 1   51 @actions = ( shift, shift // sub { $_ } );
  1         4  
41 7         18 @args = @_;
42             }
43              
44             # { $selector => $code, ... } [, @arguments ]
45             elsif ( ref $_[0] eq 'HASH' ) {
46 22         31 @actions = %{ shift @_ };
  22         72  
47 22         43 @args = @_;
48              
49             # code [, @arguments ]
50             }
51             else {
52 3995         15761 return _simple_action(@_);
53             }
54              
55 29         109 my $n = ( scalar @actions ) / 2 - 1;
56              
57             # check action functions and add arguments
58             $actions[ $_ * 2 + 1 ] = _simple_action( $actions[ $_ * 2 + 1 ], @args )
59 29         142 for 0 .. $n;
60              
61             # TODO: compile selectors for performance
62              
63             sub {
64 109     109   160 my $element = $_[0];
65              
66             # get all matching actions
67             my @matching =
68 72         701 map { $actions[ $_ * 2 + 1 ] }
69 109         656 grep { $element->match( $actions[ $_ * 2 ] ) } 0 .. $n;
  157         593  
70              
71 109         178 my @return = ();
72              
73 109         631 foreach my $action (@matching) {
74 72         114 local $_ = $_[0]; # FIXME: $doc->walk( Section => sub { $_->id } )
75 72         170 @return = ( $action->(@_) );
76             }
77              
78 109 50       306 wantarray ? @return : $return[0];
79             }
80 29         153 }
81              
82             sub transform {
83 3928     3928 1 6016 my $ast = shift;
84 3928         5699 my $action = action(@_);
85              
86 3928   100     12430 my $reftype = reftype($ast) || '';
87              
88 3928 100       8359 if ( $reftype eq 'ARRAY' ) {
    100          
89 1088         2184 for ( my $i = 0 ; $i < @$ast ; ) {
90 1759         3886 my $item = $ast->[$i];
91              
92 1759 100 100     6989 if ( ( reftype $item || '' ) eq 'HASH' and $item->{t} ) {
      100        
93 835         1391 my $res = $action->($item);
94              
95 835 100       2913 if ( defined $res ) {
96             # stop traversal
97 21 100       72 if ( $res eq \undef ) {
98 1         2 $i++;
99             # replace current item with result element(s)
100             } else {
101 20 100 50     92 my @elements = #map { transform($_, $action, @_) }
102             ( reftype $res || '' ) eq 'ARRAY' ? @$res : $res;
103 20         55 splice @$ast, $i, 1, @elements;
104 20         42 $i += scalar @elements;
105             }
106 21         160 next;
107             }
108             }
109 1738         3465 transform( $item, $action );
110 1738         3163 $i++;
111             }
112             }
113             elsif ( $reftype eq 'HASH' ) {
114              
115             # TODO: directly transform an element.
116             # if (blessed $ast and $ast->isa('Pandoc::Elements::Element')) {
117             # } else {
118 1050         3238 foreach ( keys %$ast ) {
119 2088         4563 transform( $ast->{$_}, $action, @_ );
120             }
121              
122             # }
123             }
124              
125 3928         8771 $ast;
126             }
127              
128             sub walk(@) { ## no critic
129 53     53 1 1458 my $ast = shift;
130 53         266 my $action = action(@_);
131             transform( $ast, sub {
132 597     597   875 local $_ = $_[0];
133 597         1299 my $q = $action->(@_);
134 597 50 66     2886 return (defined $q and $q eq \undef) ? \undef : undef
135 53         381 } );
136             }
137              
138             sub query(@) { ## no critic
139 33     33 1 1823 my $ast = shift;
140 33         78 my $action = action(@_);
141              
142 33         68 my $list = [];
143             transform( $ast, sub {
144 169     169   246 local $_ = $_[0];
145 169         336 my $q = $action->(@_);
146 169 100 100     726 return $q if !defined $q or $q eq \undef;
147 72         168 push @$list, $q;
148             return
149 33         150 } );
  72         150  
150 33         403 return $list;
151             }
152              
153             1;
154             __END__