File Coverage

blib/lib/Attean/API/Query.pm
Criterion Covered Total %
statement 345 383 90.0
branch 81 96 84.3
condition 30 31 96.7
subroutine 56 60 93.3
pod 0 28 0.0
total 512 598 85.6


line stmt bran cond sub pod time code
1 50     50   616 use v5.14;
  50         151  
2 50     50   331 use warnings;
  50         97  
  50         2342  
3              
4             =head1 NAME
5              
6             Attean::API::Query - Utility package defining query-related roles
7              
8             =head1 VERSION
9              
10             This document describes Attean::API::Query version 0.032
11              
12             =head1 SYNOPSIS
13              
14             use v5.14;
15             use Attean;
16              
17             =head1 DESCRIPTION
18              
19             This is a utility package for defining query-related roles:
20              
21             =over 4
22              
23             =item * L<Attean::API::DirectedAcyclicGraph>
24              
25             =cut
26              
27             use Scalar::Util qw(refaddr);
28 50     50   261 use Types::Standard qw(ArrayRef ConsumerOf);
  50         88  
  50         1833  
29 50     50   255  
  50         97  
  50         272  
30             use Moo::Role;
31 50     50   24462  
  50         97  
  50         356  
32             # =item C<< children >>
33             #
34             # An ARRAY reference of L<Attean::API::DirectedAcyclicGraph> objects.
35             #
36             # =back
37             #
38             # =cut
39              
40             has 'children' => (
41             is => 'ro',
42             isa => ArrayRef[ConsumerOf['Attean::API::DirectedAcyclicGraph']],
43             default => sub { [] },
44             );
45            
46             # =item C<< is_leaf >>
47             #
48             # Returns true if the referent has zero C<< children >>, false otherwise.
49             #
50             # =cut
51              
52             my $self = shift;
53             return not(scalar(@{ $self->children }));
54 3     3 0 1172 }
55 3         7
  3         17  
56             # =item C<< walk( prefix => \&pre_cb, postfix => \&pre_cb ) >>
57             #
58             # Walks the graph rooted at the referent, calling C<< &pre_cb >> (if supplied)
59             # before descending, and C<< &post_cb >> (if supplied) after descending. The
60             # callback functions are passed the current graph walk node as the single
61             # argument.
62             #
63             # =cut
64              
65             my $self = shift;
66             my %args = @_;
67             my $level = $args{ level } // 0;
68 150     150 0 215 my $parent = $args{ parent };
69 150         369 if (my $cb = $args{ prefix }) {
70 150   100     407 $cb->( $self, $level, $parent );
71 150         207 }
72 150 50       295 foreach my $c (@{ $self->children }) {
73 150         251 $c->walk( %args, level => (1+$level), parent => $self );
74             }
75 150         977 if (my $cb = $args{ postfix }) {
  150         379  
76 99         373 $cb->( $self, $level, $parent );
77             }
78 150 100       437 }
79 2         6
80             # =item C<< has_only_subtree_types( @classes ) >>
81             #
82             # Returns true if the invocant and all of its sub-trees are instances of only
83             # the listed classes, false otherwise.
84             #
85             # =cut
86              
87             my $self = shift;
88             my @types = @_;
89             my %types = map { $_ => 1 } @types;
90             return 0 unless (exists $types{ ref($self) });
91 2     2 0 11
92 2         5 my %classes;
93 2         4 $self->walk( prefix => sub {
  2         6  
94 2 100       11 my $plan = shift;
95             $classes{ref($plan)}++;
96 1         2 });
97             foreach my $type (@types) {
98 1     1   3 delete $classes{$type};
99 1         4 }
100 1         7 my @keys = keys %classes;
101 1         4 return (scalar(@keys) == 0) ? 1 : 0;
102 1         3 }
103              
104 1         4 # =item C<< cover( prefix => \&pre_cb, postfix => \&pre_cb ) >>
105 1 50       7 #
106             # Similar to C<< walk >>, walks the graph rooted at the referent, calling
107             # C<< &pre_cb >> (if supplied) before descending, and C<< &post_cb >> (if
108             # supplied) after descending. However, unlike C<< walk >>, each node in the graph
109             # is visited only once.
110             #
111             # =cut
112              
113             my $self = shift;
114             return $self->_cover({}, @_);
115             }
116            
117             my $self = shift;
118 1     1 0 2 my $seen = shift;
119 1         5 my %cb = @_;
120             return if ($seen->{refaddr($self)}++);
121             if (my $cb = $cb{ prefix }) {
122             $cb->( $self );
123 3     3   4 }
124 3         4 foreach my $c (@{ $self->children }) {
125 3         6 $c->_cover( $seen, %cb );
126 3 100       13 }
127 2 50       6 if (my $cb = $cb{ postfix }) {
128 2         5 $cb->( $self );
129             }
130 2         6 }
  2         6  
131 2         8  
132             my $self = shift;
133 2 50       6 my @types = @_;
134 0         0 my @p;
135             $self->walk( prefix => sub {
136             my $a = shift;
137             foreach my $t (@types) {
138             push(@p, $a) if ($a->isa($t) or $a->does($t));
139 11     11 0 1371 }
140 11         22 });
141 11         17 return @p;
142             }
143 43     43   49 }
144 43         57  
145 43 100 66     202 use AtteanX::SPARQL::Constants;
146             use AtteanX::SPARQL::Token;
147 11         74 use Encode qw(decode_utf8);
148 11         63 use Attean::API::Iterator;
149             use Attean::API::Serializer;
150             use AtteanX::Serializer::SPARQL;
151              
152             use Moo::Role;
153 50     50   37723  
  50         106  
  50         6244  
154 50     50   295 requires 'sparql_tokens';
  50         99  
  50         956  
155 50     50   23141
  50         491276  
  50         3294  
156 50     50   19286 my $self = shift;
  50         152  
  50         1737  
157 50     50   20460 my $s = AtteanX::Serializer::SPARQL->new();
  50         179  
  50         2336  
158 50     50   21878 my $i = $self->sparql_tokens;
  50         133  
  50         1343  
159             my $bytes = $s->serialize_iter_to_bytes($i);
160 50     50   328 return decode_utf8($bytes);
  50         90  
  50         217  
161             }
162            
163             my $self = shift;
164             if ($self->does('Attean::API::SPARQLQuerySerializable')) {
165 54     54 0 18611 my $l = AtteanX::SPARQL::Token->lbrace;
166 54         1466 my $r = AtteanX::SPARQL::Token->rbrace;
167 54         9099 my @tokens;
168 54         2289 push(@tokens, $l);
169 54         1022 push(@tokens, $self->sparql_tokens->elements);
170             push(@tokens, $r);
171             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
172             } else {
173 29     29 0 60 return $self->sparql_tokens;
174 29 50       91 }
175 0         0 }
176 0         0
177 0         0 my $self = shift;
178 0         0 my $dataset = shift;
179 0         0 my @default = @{ $dataset->{ default } || [] };
180 0         0 my @named = @{ $dataset->{ named } || [] };
181 0         0 my $has_dataset = (scalar(@default) + scalar(@named));
182             my @tokens;
183 29         674 if ($has_dataset) {
184             my $from = AtteanX::SPARQL::Token->keyword('FROM');
185             my $named = AtteanX::SPARQL::Token->keyword('NAMED');
186             foreach my $i (sort { $a->as_string cmp $b->as_string } @default) {
187             push(@tokens, $from);
188 33     33 0 61 push(@tokens, $i->sparql_tokens->elements);
189 33         51 }
190 33 100       55 foreach my $i (sort { $a->as_string cmp $b->as_string } @named) {
  33         155  
191 33 100       58 push(@tokens, $from);
  33         121  
192 33         88 push(@tokens, $named);
193 33         55 push(@tokens, $i->sparql_tokens->elements);
194 33 100       82 }
195 4         14 }
196 4         170 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
197 4         162 }
  0         0  
198 4         9
199 4         24 my $self = shift;
200             my %args = @_;
201 4         13 my $dataset = $args{dataset} || {};
  0         0  
202 4         10
203 4         8 my $as = AtteanX::SPARQL::Token->keyword('AS');
204 4         13 my $lparen = AtteanX::SPARQL::Token->lparen;
205             my $rparen = AtteanX::SPARQL::Token->rparen;
206            
207 33         563 my $algebra = $self;
208            
209             my %modifiers;
210             my $form = 'SELECT';
211 33     33 0 58 if ($algebra->isa('Attean::Algebra::Ask')) {
212 33         77 $form = 'ASK';
213 33   100     149 ($algebra) = @{ $algebra->children };
214             } elsif ($algebra->isa('Attean::Algebra::Describe')) {
215 33         147 $form = 'DESCRIBE';
216 33         1773 $modifiers{describe} = $algebra->terms;
217 33         1350 ($algebra) = @{ $algebra->children };
218             } elsif ($algebra->isa('Attean::Algebra::Construct')) {
219 33         1294 $form = 'CONSTRUCT';
220             $modifiers{construct} = $algebra->triples;
221 33         57 ($algebra) = @{ $algebra->children };
222 33         72 }
223 33 100       519
    100          
    100          
224 3         35 unless ($form eq 'CONSTRUCT' or $form eq 'DESCRIBE') {
225 3         14 while ($algebra->isa('Attean::Algebra::Extend') or $algebra->isa('Attean::Algebra::Group') or $algebra->isa('Attean::Algebra::OrderBy') or $algebra->isa('Attean::Algebra::Distinct') or $algebra->isa('Attean::Algebra::Reduced') or $algebra->isa('Attean::Algebra::Slice') or $algebra->isa('Attean::Algebra::Project')) {
  3         14  
226             # TODO: Handle HAVING
227 3         7 # TODO: Error if Slice appears before distinct/reduced
228 3         13 if ($algebra->isa('Attean::Algebra::Distinct')) {
229 3         7 $modifiers{ distinct } = 1;
  3         10  
230             } elsif ($algebra->isa('Attean::Algebra::Reduced')) {
231 3         10 $modifiers{ reduced } = 1;
232 3         15 } elsif ($algebra->isa('Attean::Algebra::Slice')) {
233 3         6 if ($algebra->limit >= 0) {
  3         13  
234             $modifiers{ limit } = $algebra->limit;
235             }
236 33 100 100     195 if ($algebra->offset > 0) {
237 27   100     675 $modifiers{ offset } = $algebra->offset;
      100        
      100        
      100        
      100        
      100        
238             }
239             } elsif ($algebra->isa('Attean::Algebra::OrderBy')) {
240 30 100       336 $modifiers{order} = $algebra->comparators;
    100          
    100          
    100          
    100          
    100          
    50          
241 4         9 } elsif ($algebra->isa('Attean::Algebra::Extend')) {
242             my $v = $algebra->variable;
243 2         5 my $name = $v->value;
244             my $expr = $algebra->expression;
245 6 50       23 my @tokens;
246 6         17 push(@tokens, $lparen);
247             push(@tokens, $expr->sparql_tokens->elements);
248 6 100       18 push(@tokens, $as);
249 4         11 push(@tokens, $v->sparql_tokens->elements);
250             push(@tokens, $rparen);
251             $modifiers{project_expression_tokens}{$name} = \@tokens;
252 1         5 } elsif ($algebra->isa('Attean::Algebra::Project')) {
253             my $vars = $algebra->variables;
254 4         12 my ($child) = @{ $algebra->children };
255 4         10 my @vars = sort(map { $_->value } @$vars);
256 4         9 my @subvars = sort($child->in_scope_variables);
257 4         5 if (scalar(@vars) == scalar(@subvars) and join('.', @vars) eq join('.', @subvars)) {
258 4         15 # this is a SELECT * query
259 4         17 } else {
260 4         17 foreach my $v (@$vars) {
261 4         16 my $name = $v->value;
262 4         33 unless ($modifiers{project_variables}{$name}++) {
263 4         20 push(@{ $modifiers{project_variables_order} }, $name);
264             }
265 12         53 }
266 12         18 }
  12         39  
267 12         31 } elsif ($algebra->isa('Attean::Algebra::Group')) {
  22         88  
268 12         55 my $aggs = $algebra->aggregates;
269 12 100 100     811 my $groups = $algebra->groupby;
270             foreach my $agg (@$aggs) {
271             my $v = $agg->variable;
272 7         199 my $name = $v->value;
273 13         25 my @tokens;
274 13 50       43 push(@tokens, $lparen);
275 13         19 push(@tokens, $agg->sparql_tokens->elements);
  13         39  
276             push(@tokens, $as);
277             push(@tokens, $v->sparql_tokens->elements);
278             push(@tokens, $rparen);
279             unless ($modifiers{project_variables}{$name}++) {
280 1         4 push(@{ $modifiers{project_variables_order} }, $name);
281 1         6 }
282 1         6 $modifiers{project_expression_tokens}{$name} = \@tokens;
283 1         4 }
284 1         7 foreach my $group (@$groups) {
285 1         3 push(@{ $modifiers{groups} }, $group->sparql_tokens->elements);
286 1         3 }
287 1         6 } else {
288 1         4 die "Unexpected pattern type encountered in query_tokens: " . ref($algebra);
289 1         5 }
290 1         3 ($algebra) = @{ $algebra->children };
291 1 50       7 }
292 1         2 }
  1         4  
293            
294 1         5 my @tokens;
295              
296 1         3 my $where = AtteanX::SPARQL::Token->keyword('WHERE');
297 1         3 my $lbrace = AtteanX::SPARQL::Token->lbrace;
  1         6  
298             my $rbrace = AtteanX::SPARQL::Token->rbrace;
299              
300 0         0  
301             if ($form eq 'SELECT') {
302 30         53 push(@tokens, AtteanX::SPARQL::Token->keyword('SELECT'));
  30         506  
303             if ($modifiers{distinct}) {
304             push(@tokens, AtteanX::SPARQL::Token->keyword('DISTINCT'));
305             } elsif ($modifiers{reduced}) {
306 33         66 push(@tokens, AtteanX::SPARQL::Token->keyword('REDUCED'));
307             }
308 33         115
309 33         1557 if (my $p = $modifiers{project_variables_order}) {
310 33         1395 foreach my $name (@$p) {
311             if (my $etokens = $modifiers{project_expression_tokens}{$name}) {
312             push(@tokens, @$etokens);
313 33 100       1376 } else {
    100          
    100          
    50          
314 24         77 my $v = Attean::Variable->new( value => $name );
315 24 100       1035 push(@tokens, $v->sparql_tokens->elements);
    100          
316 4         13 }
317             }
318 2         7 } else {
319             push(@tokens, AtteanX::SPARQL::Token->star);
320             }
321 24 100       289
322 8         20 push(@tokens, $self->dataset_tokens($dataset)->elements);
323 14 100       41
324 5         21 push(@tokens, $where);
325             if ($algebra->isa('Attean::Algebra::Join')) {
326 9         146 # don't emit extraneous braces at the top-level
327 9         452 push(@tokens, $algebra->sparql_tokens->elements);
328             } else {
329             push(@tokens, $lbrace);
330             push(@tokens, $algebra->sparql_tokens->elements);
331 16         64 push(@tokens, $rbrace);
332             }
333             if (my $groups = $modifiers{groups}) {
334 24         723 push(@tokens, AtteanX::SPARQL::Token->keyword('GROUP'));
335             push(@tokens, AtteanX::SPARQL::Token->keyword('BY'));
336 24         82 push(@tokens, @$groups);
337 24 100       148 }
338             if (my $expr = $modifiers{having}) {
339 1         7 push(@tokens, AtteanX::SPARQL::Token->keyword('HAVING'));
340             push(@tokens, $expr->sparql_tokens->elements);
341 23         56 }
342 23         93 if (my $comps = $modifiers{order}) {
343 23         93 push(@tokens, AtteanX::SPARQL::Token->keyword('ORDER'));
344             push(@tokens, AtteanX::SPARQL::Token->keyword('BY'));
345 24 100       105 foreach my $c (@$comps) {
346 1         5 push(@tokens, $c->sparql_tokens->elements);
347 1         48 }
348 1         41 }
349             if (exists $modifiers{limit}) {
350 24 50       69 push(@tokens, AtteanX::SPARQL::Token->keyword('LIMIT'));
351 0         0 push(@tokens, AtteanX::SPARQL::Token->integer($modifiers{limit}));
352 0         0 }
353             if (exists $modifiers{offset}) {
354 24 100       61 push(@tokens, AtteanX::SPARQL::Token->keyword('OFFSET'));
355 1         5 push(@tokens, AtteanX::SPARQL::Token->integer($modifiers{offset}));
356 1         51 }
357 1         45 } elsif ($form eq 'DESCRIBE') {
358 1         6 push(@tokens, AtteanX::SPARQL::Token->keyword('DESCRIBE'));
359             foreach my $t (@{ $modifiers{describe} }) {
360             push(@tokens, $t->sparql_tokens->elements);
361 24 100       94 }
362 6         19 push(@tokens, $self->dataset_tokens($dataset)->elements);
363 6         278 push(@tokens, $where);
364             push(@tokens, $lbrace);
365 24 100       304 push(@tokens, $algebra->sparql_tokens->elements);
366 4         12 push(@tokens, $rbrace);
367 4         160 } elsif ($form eq 'CONSTRUCT') {
368             push(@tokens, AtteanX::SPARQL::Token->keyword('CONSTRUCT'));
369             push(@tokens, $lbrace);
370 3         10 foreach my $t (@{ $modifiers{construct} }) {
371 3         127 push(@tokens, $t->sparql_tokens->elements);
  3         10  
372 5         22 push(@tokens, AtteanX::SPARQL::Token->dot);
373             }
374 3         21 push(@tokens, $rbrace);
375 3         10 push(@tokens, $self->dataset_tokens($dataset)->elements);
376 3         9 push(@tokens, $where);
377 3         15 push(@tokens, $lbrace);
378 3         31 push(@tokens, $algebra->sparql_tokens->elements);
379             push(@tokens, $rbrace);
380 3         13 } elsif ($form eq 'ASK') {
381 3         122 push(@tokens, AtteanX::SPARQL::Token->keyword('ASK'));
382 3         6 push(@tokens, $self->dataset_tokens($dataset)->elements);
  3         10  
383 3         15 push(@tokens, $lbrace);
384 3         19 push(@tokens, $algebra->sparql_tokens->elements);
385             push(@tokens, $rbrace);
386 3         141 } else {
387 3         13 die "Unexpected query for '$form' in query_tokens";
388 3         10 }
389 3         7 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
390 3         17 }
391 3         13 }
392              
393 3         13 use Moo::Role;
394 3         132 use namespace::clean;
395 3         10 with 'Attean::API::SPARQLSerializable';
396 3         15  
397 3         16 my $self = shift;
398             return $self->query_tokens;
399 0         0 }
400             }
401 33         665  
402             =item * L<Attean::API::Algebra>
403              
404             =cut
405              
406 50     50   95233 use Moo::Role;
  50         96  
  50         211  
407 50     50   16725 use Types::Standard qw(ArrayRef ConsumerOf);
  50         110  
  50         325  
408            
409             with 'Attean::API::SPARQLSerializable';
410             has 'hints' => (is => 'rw', isa => ArrayRef[ArrayRef[ConsumerOf['Attean::API::Term']]], default => sub { [] });
411 26     26 0 152
412 26         80 requires 'as_sparql';
413             requires 'in_scope_variables'; # variables that will be in-scope after this operation is evaluated
414            
415             my $self = shift;
416             return unless (scalar(@{ $self->children }) == 1);
417             return $self->children->[0];
418             }
419            
420             my $self = shift;
421 50     50   28709 return "$self";
  50         103  
  50         215  
422 50     50   16603 }
  50         131  
  50         351  
423            
424             my $self = shift;
425             my $string = '';
426             $self->walk( prefix => sub {
427             my $a = shift;
428             my $level = shift;
429             my $parent = shift;
430             my $indent = ' ' x $level;
431 2     2 0 5 $string .= "-$indent " . $a->algebra_as_string($level) . "\n";
432 2 100       2 });
  2         12  
433 1         6 return $string;
434             }
435            
436             my $self = shift;
437 0     0 0 0 my %blanks;
438 0         0 $self->walk( prefix => sub {
439             my $a = shift;
440             if ($a->isa('Attean::Algebra::BGP')) {
441             my @triples = @{ $a->triples };
442 20     20 0 2111 my @nodes = grep { $_->does('Attean::API::Blank') } map { $_->values } @triples;
443 20         41 foreach my $b (@nodes) {
444             $blanks{ $b->value } = $b;
445 74     74   94 }
446 74         90 } elsif ($a->isa('Attean::Algebra::Path')) {
447 74         83 my @nodes = grep { $_->does('Attean::API::Blank') } ($a->subject, $a->object);
448 74         135 foreach my $b (@nodes) {
449 74         291 $blanks{ $b->value } = $b;
450 20         148 }
451 20         155 }
452             });
453             return values %blanks;
454             }
455 4     4 0 8
456 4         6 if ($ENV{ATTEAN_TYPECHECK}) {
457             around 'BUILD' => sub {
458 4     4   8 my $orig = shift;
459 4 50       53 my $self = shift;
    50          
460 0         0 $self->$orig(@_);
  0         0  
461 0         0 my $name = ref($self);
  0         0  
  0         0  
462 0         0 $name =~ s/^.*://;
463 0         0 if ($self->can('arity')) {
464             my $arity = $self->arity;
465             my $children = $self->children;
466 0         0 my $size = scalar(@$children);
  0         0  
467 0         0 unless ($size == $arity) {
468 0         0 Carp::confess "${name} algebra construction with bad number of children (expected $arity, but got $size)";
469             }
470             }
471 4         41 }
472 4         36 }
473             }
474              
475       502 0   =item * L<Attean::API::QueryTree>
476              
477             =cut
478              
479             use Moo::Role;
480             with 'Attean::API::DirectedAcyclicGraph';
481             }
482              
483             =item * L<Attean::API::NullaryQueryTree>
484              
485             =cut
486              
487             use Moo::Role;
488             with 'Attean::API::QueryTree';
489             }
490              
491             =item * L<Attean::API::UnaryQueryTree>
492              
493             =cut
494              
495             use Moo::Role;
496             with 'Attean::API::QueryTree';
497            
498             my $self = shift;
499             return $self->children->[0];
500 50     50   58556 }
  50         115  
  50         260  
501             }
502              
503             =item * L<Attean::API::BinaryQueryTree>
504              
505             =cut
506              
507             use Moo::Role;
508             with 'Attean::API::QueryTree';
509 50     50   17362 }
  50         100  
  50         215  
510 1     1 0 5  
511             =item * L<Attean::API::PropertyPath>
512              
513             =cut
514              
515             use Moo::Role;
516             with 'Attean::API::QueryTree';
517             requires 'as_string';
518             requires 'as_sparql';
519 50     50   17409 }
  50         105  
  50         229  
520 2     2 0 337  
521             =item * L<Attean::API::UnaryPropertyPath>
522              
523             =cut
524 36     36 0 11033  
525 36         284 use Types::Standard qw(ConsumerOf);
526              
527             use Moo::Role;
528              
529             # has 'path' => (is => 'ro', isa => ConsumerOf['Attean::API::PropertyPath'], required => 1);
530             my $self = shift;
531             my ($path) = @{ $self->children };
532             my $pstr = $path->as_string;
533             if ($path->does('Attean::API::UnaryPropertyPath')) {
534 50     50   18927 $pstr = "($pstr)";
  50         138  
  50         198  
535 0     0 0 0 }
536             my $str = sprintf("%s%s%s", $self->prefix_name, $pstr, $self->postfix_name);
537             return $str;
538             }
539             my $self = shift;
540             return "Property Path " . $self->prefix_name . $self->postfix_name;
541             }
542             with 'Attean::API::PropertyPath', 'Attean::API::UnaryQueryTree';
543             }
544 50     50   17646  
  50         99  
  50         176  
545             =item * L<Attean::API::NaryPropertyPath>
546              
547             =cut
548              
549             use Types::Standard qw(ArrayRef ConsumerOf);
550              
551             use Moo::Role;
552              
553             # has 'children' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::PropertyPath']], required => 1);
554             requires 'separator';
555 50     50   18841 my $self = shift;
  50         112  
  50         276  
556             my @children = @{ $self->children };
557 50     50   21903 if (scalar(@children) == 1) {
  50         103  
  50         216  
558             return $children[0]->as_string;
559 0     0 0 0 } else {
560             return sprintf("(%s)", join($self->separator, map { $_->as_string } @children));
561 3     3 0 17 }
562 4     4 0 20 }
563             my $self = shift;
564 5     5 0 3529 return "Property Path " . $self->separator;
565 5         8 }
  5         12  
566 5         15 with 'Attean::API::PropertyPath';
567 5 100       69 }
568 1         18  
569             =item * L<Attean::API::UnionScopeVariables>
570 5         95  
571 5         16 =cut
572              
573             use Moo::Role;
574 2     2 0 4 my $self = shift;
575 2         18 my $set = Set::Scalar->new();
576             foreach my $c (@{ $self->children }) {
577             $set->insert( $c->in_scope_variables );
578             }
579             return $set->elements;
580             }
581             }
582              
583             =item * L<Attean::API::IntersectionScopeVariables>
584              
585 50     50   24663 =cut
  50         123  
  50         282  
586              
587 50     50   24998 use Moo::Role;
  50         103  
  50         196  
588             my $self = shift;
589             my @c = @{ $self->children };
590             return unless scalar(@c);
591             my $set = Set::Scalar->new(shift(@c)->in_scope_variables);
592 7     7 0 5390 foreach my $c (@c) {
593 7         10 my $rhs = Set::Scalar->new($c->in_scope_variables);
  7         21  
594 7 100       21 $set = $set->intersection($rhs);
595 2         7 }
596             return $set->elements;
597 5         17 }
  10         46  
598             }
599              
600             1;
601 2     2 0 6  
602 2         10 =back
603              
604             =head1 BUGS
605              
606             Please report any bugs or feature requests to through the GitHub web interface
607             at L<https://github.com/kasei/attean/issues>.
608              
609             =head1 SEE ALSO
610              
611              
612 50     50   23622  
  50         103  
  50         228  
613             =head1 AUTHOR
614 21     21 0 88  
615 21         116 Gregory Todd Williams C<< <gwilliams@cpan.org> >>
616 21         1319  
  21         68  
617 28         854 =head1 COPYRIGHT
618              
619 21         2503 Copyright (c) 2014--2022 Gregory Todd Williams.
620             This program is free software; you can redistribute it and/or modify it under
621             the same terms as Perl itself.
622              
623             =cut