File Coverage

blib/lib/Text/KDL/XS/Document.pm
Criterion Covered Total %
statement 53 56 94.6
branch 14 22 63.6
condition 1 2 50.0
subroutine 10 10 100.0
pod 2 3 66.6
total 80 93 86.0


line stmt bran cond sub pod time code
1             package Text::KDL::XS::Document;
2              
3 7     7   46 use strict;
  7         12  
  7         279  
4 7     7   32 use warnings;
  7         10  
  7         384  
5              
6 7     7   42 use Carp ();
  7         31  
  7         164  
7 7     7   26 use Text::KDL::XS::Node;
  7         11  
  7         225  
8 7     7   40 use Text::KDL::XS::Value;
  7         9  
  7         4076  
9              
10             sub new {
11 19     19 0 31 my ($class, %args) = @_;
12 19   50     103 return bless { nodes => $args{nodes} // [] }, $class;
13             }
14              
15 29     29 1 660 sub nodes { $_[0]->{nodes} }
16              
17             # Drive a parser to assemble a full document tree.
18             # Treats each event as a guarded transition; bails fast on illegal sequences.
19             sub _build_from_parser {
20 19     19   47 my ($class, $parser) = @_;
21              
22 19         46 my $doc = $class->new;
23 19         40 my @stack; # nodes whose children we're currently filling
24             my $current; # node we're attaching args/props to (top of stack)
25              
26 19         65 while (defined(my $ev = $parser->next_event)) {
27 176         339 my $kind = $ev->{event};
28              
29 176 100       272 if ($kind eq 'start_node') {
30             my $node = Text::KDL::XS::Node->new(
31             name => $ev->{name},
32             type_annotation => $ev->{type},
33 58         207 );
34 58 100       139 if ($current) {
35 19         40 $current->_push_child($node);
36             }
37             else {
38 39         46 push @{ $doc->{nodes} }, $node;
  39         88  
39             }
40 58         84 push @stack, $node;
41 58         70 $current = $node;
42 58         186 next;
43             }
44              
45 118 100       213 if ($kind eq 'end_node') {
46 57 50       112 Carp::croak("KDL: end_node with empty stack") unless @stack;
47 57         64 pop @stack;
48 57         75 $current = $stack[-1];
49 57         124 next;
50             }
51              
52 61 100       100 if ($kind eq 'argument') {
53 57 50       108 Carp::croak("KDL: argument outside any node") unless $current;
54 57         115 $current->_push_arg(_value_from_event($ev->{value}));
55 57         172 next;
56             }
57              
58 4 50       6 if ($kind eq 'property') {
59 4 50       7 Carp::croak("KDL: property outside any node") unless $current;
60 4         6 $current->_push_prop($ev->{name}, _value_from_event($ev->{value}));
61 4         10 next;
62             }
63              
64             # Comments only appear when emit_comments is set; we currently
65             # discard them at the tree layer. Streaming users can opt in.
66 0 0       0 next if $kind eq 'comment';
67              
68 0         0 Carp::croak("KDL: unexpected event '$kind'");
69             }
70              
71 17 50       66 Carp::croak("KDL: input ended with " . scalar(@stack) . " unclosed node(s)")
72             if @stack;
73              
74 17         158 return $doc;
75             }
76              
77             sub _value_from_event {
78 61     61   89 my ($v) = @_;
79 61 50       208 return $v if ref($v) eq 'Text::KDL::XS::Value';
80             # XS already blesses; this guard exists only for hand-built events.
81 0         0 return Text::KDL::XS::Value->new(%$v);
82             }
83              
84             sub as_data {
85 3     3 1 9 my ($self) = @_;
86 3         4 return [ map { $_->as_data } @{ $self->{nodes} } ];
  6         11  
  3         7  
87             }
88              
89             1;
90              
91             __END__