File Coverage

blib/lib/Catmandu/Emit.pm
Criterion Covered Total %
statement 85 102 83.3
branch 15 28 53.5
condition 2 5 40.0
subroutine 21 23 91.3
pod n/a
total 123 158 77.8


line stmt bran cond sub pod time code
1             package Catmandu::Emit;
2              
3             # eval context ->
4 154     154   93798 use Catmandu::Sane;
  154         464  
  154         1575  
5              
6             our $VERSION = '1.2020';
7              
8 154     154   1542 use Catmandu::Util qw(:is :string require_package);
  154         450  
  154         51082  
9 154     154   1363 use Clone qw(clone);
  154         476  
  154         16062  
10             require Catmandu; # avoid circular dependencies
11              
12             sub _eval_emit {
13 1104     1104   246549 eval $_[0];
14             }
15              
16             # <- eval context
17              
18 154     154   1351 use B ();
  154         506  
  154         3642  
19 154     154   896 use Moo::Role;
  154         433  
  154         1540  
20              
21             # global state ->
22             sub _reject {
23 957     957   2572 state $reject = {};
24             }
25              
26             sub _generate_label {
27 962     962   1598 state $num_labels = 0;
28 962         2249 my $label = "__CATMANDU__FIX__${num_labels}";
29 962         1621 $num_labels++;
30 962         2415 $label;
31             }
32              
33             sub _reject_label {
34 504     504   1584 state $reject_label = _generate_label;
35             }
36              
37             sub _generate_var {
38 5158     5158   10472 state $num_vars = 0;
39 5158         10346 my $var = "\$__catmandu__${num_vars}";
40 5158         7254 $num_vars++;
41 5158         10725 $var;
42             }
43              
44             # <- global state
45              
46             sub _eval_sub {
47 1104     1104   15829 my ($self, @args) = @_;
48 1104         1877 local $@;
49 1104 50       3013 _eval_emit($self->_emit_sub(@args)) or Catmandu::Error->throw($@);
50             }
51              
52             sub _emit_sub {
53 1104     1104   3542 my ($self, $body, %opts) = @_;
54 1104   100     3647 my $captures = $opts{captures} ||= {};
55 1104         1817 my $perl = "sub {";
56 1104 50       2639 if (my $args = $opts{args}) {
57 1104         3846 $perl .= 'my (' . join(', ', @$args) . ') = @_;';
58             }
59 1104         2414 $perl .= $body;
60 1104         1822 $perl .= "};";
61             my @captured_vars = map {
62 1104         3240 $self->_emit_declare_vars($_,
  1912         4142  
63             '$_[1]->{' . $self->_emit_string($_) . '}');
64             } keys %$captures;
65 1104         3663 $perl = join('', @captured_vars, $perl);
66              
67 1104         4155 return $perl, $captures;
68             }
69              
70             sub _emit_declare_vars {
71 2596     2596   5184 my ($self, $var, $val) = @_;
72 2596 50       6229 $var = "(" . join(", ", @$var) . ")" if is_array_ref($var);
73 2596 50       5209 $val = "(" . join(", ", @$val) . ")" if is_array_ref($val);
74 2596 50       4846 if (defined $val) {
75 2596         10207 return "my ${var} = ${val};";
76             }
77 0         0 "my ${var};";
78             }
79              
80             sub _emit_branch {
81 145     145   429 my ($self, $test, $pass, $fail) = @_;
82 145         767 "if (${test}) {${pass}} else {${fail}}";
83             }
84              
85             sub _emit_call {
86 703     703   2865 my ($self, $sub_var, @args) = @_;
87 703         3345 "${sub_var}->(" . join(', ', @args) . ")";
88             }
89              
90             sub _emit_iterate_array {
91 37     37   141 my ($self, $var, $cb) = @_;
92 37         89 my $perl = "";
93 37         116 my $i = $self->_generate_var;
94              
95             # loop backwards so that deletions are safe
96 37         219 $perl .= "for (my ${i} = \@{${var}} - 1; ${i} >= 0; ${i}--) {";
97 37         280 $perl .= $cb->("${var}->[${i}]", up_var => $var, index => $i);
98 37         141 $perl .= "}";
99 37         162 $perl;
100             }
101              
102             sub _emit_iterate_hash {
103 0     0   0 my ($self, $var, $cb) = @_;
104 0         0 my $perl = "";
105 0         0 my $k = $self->generate_var;
106              
107 0         0 $perl .= "for my ${k} (keys(\%{${var}})) {";
108 0         0 $perl .= $cb->("${var}->{${k}}", up_var => $var, key => $k);
109 0         0 $perl .= "}";
110 0         0 $perl;
111             }
112              
113             sub _emit_assign_cb {
114 169     169   645 my ($self, $var, $cb_var, %opts) = @_;
115 169         467 my $val_var = $self->_generate_var;
116 169         445 my $cancel_var = $self->_generate_var;
117 169         431 my $delete_var = $self->_generate_var;
118 169         351 my $perl = "";
119 169         735 $perl
120             .= "my (${val_var}, ${cancel_var}, ${delete_var}) = ${cb_var}->(${var});";
121 169         417 $perl .= "if (${delete_var}) {";
122 169         556 $perl .= $self->_emit_delete(%opts);
123 169         519 $perl .= "} elsif (!${cancel_var}) {";
124 169         576 $perl .= $self->_emit_assign($var, $val_var, %opts);
125 169         336 $perl .= "}";
126 169         1034 $perl;
127             }
128              
129             sub _emit_assign {
130 727     727   1856 my ($self, $var, $val, %opts) = @_;
131 727         1168 my $l_var = $var;
132 727 100       2012 if (my $up_var = $opts{up_var}) {
133 167 100       503 if (defined(my $key = $opts{key})) {
    50          
134 155         449 $l_var = "${up_var}->{${key}}";
135             }
136             elsif (defined(my $index = $opts{index})) {
137 12         43 $l_var = "${up_var}->[${index}]";
138             }
139             else {
140 0         0 Catmandu::BadArg->throw('up_var without key or index');
141             }
142             }
143 727         3216 "${l_var} = ${val};";
144             }
145              
146             sub _emit_delete {
147 169     169   528 my ($self, %opts) = @_;
148 169         362 my $up_var = $opts{up_var};
149 169 100       694 if (!defined($up_var)) {
    100          
    50          
150              
151             # TODO deleting the root object is equivalent to reject
152 2         10 $self->_emit_reject;
153             }
154             elsif (defined(my $key = $opts{key})) {
155 155         660 "delete ${up_var}->{${key}}";
156             }
157             elsif (defined(my $idx = $opts{index})) {
158 12         67 "splice(\@{${up_var}}, ${idx}, 1)";
159             }
160             else {
161 0         0 Catmandu::BadArg->throw('up_var without key or index');
162             }
163             }
164              
165             sub _emit_value {
166 0     0   0 my ($self, $val) = @_;
167              
168             ## undef
169 0 0       0 return 'undef' unless defined $val;
170              
171             ## numbers
172             # we don't quote ints and floats unless there are leading
173             # (and for floats trailing) zero's
174 0 0       0 if (is_integer($val)) {
175 0         0 return $val;
176             }
177 0 0 0     0 if (is_float($val) && $val !~ /0$/) {
178 0         0 return $val;
179             }
180              
181             ## strings
182 0         0 $self->_emit_string($val);
183             }
184              
185             sub _emit_string {
186 2734     2734   5023 my ($self, $str) = @_;
187 2734         10709 B::perlstring($str);
188             }
189              
190             sub _emit_reject {
191 28     28   71 my ($self) = @_;
192 28         89 'goto ' . $self->_reject_label . ';';
193             }
194              
195             1;
196              
197             __END__
198              
199             =pod
200              
201             =head1 NAME
202              
203             Catmandu::Emit - Role with helper methods for code emitting
204              
205             =cut