File Coverage

examples/parse-xml.pl
Criterion Covered Total %
statement 61 64 95.3
branch 7 10 70.0
condition n/a
subroutine 18 21 85.7
pod n/a
total 86 95 90.5


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3 1     1   470172 use v5.14;
  1         4  
4 1     1   10 use warnings;
  1         2  
  1         97  
5              
6             # DO NOT RELY ON THIS AS A REAL XML PARSER!
7              
8             # It is not intended to be used actually as an XML parser, simply to stand as
9             # an example of how you might use Parser::MGC to parse an XML-like syntax
10              
11             # There are a great many things it doesn't do correctly; it lacks at least the
12             # following features:
13             # Entities
14             # Processing instructions
15             # Comments
16             # CDATA
17              
18             package XmlParser;
19 1     1   28 use base qw( Parser::MGC );
  1         3  
  1         737  
20              
21             sub parse
22             {
23 5     5   11 my $self = shift;
24              
25 5         19 my $rootnode = $self->parse_node;
26 5 50       19 $rootnode->kind eq "element" or die "Expected XML root node";
27 5 50       16 $rootnode->name eq "xml" or die "Expected XML root node";
28              
29 5         18 return [ $rootnode->children ];
30             }
31              
32             sub parse_node
33             {
34 28     28   50 my $self = shift;
35              
36             # A "node" is either an XML element subtree or plaintext
37 28         81 $self->any_of( 'parse_plaintext', 'parse_element' );
38             }
39              
40             sub parse_plaintext
41             {
42 28     28   48 my $self = shift;
43              
44 28         75 my $str = $self->substring_before( '<' );
45 28 100       114 $self->fail( "No plaintext" ) unless length $str;
46              
47 7         25 return XmlParser::Node::Plain->new( $str );
48             }
49              
50             sub parse_element
51             {
52 21     21   32 my $self = shift;
53              
54 21         78 my $tag = $self->parse_tag;
55              
56 11         40 $self->commit;
57              
58 11 100       34 return XmlParser::Node::Element->new( $tag->{name}, $tag->{attrs} ) if $tag->{selfclose};
59              
60 10         24 my $childlist = $self->sequence_of( 'parse_node' );
61              
62             $self->parse_close_tag->{name} eq $tag->{name}
63 10 50       28 or $self->fail( "Expected $tag->{name} to be closed" );
64              
65 10         58 return XmlParser::Node::Element->new( $tag->{name}, $tag->{attrs}, @$childlist );
66             }
67              
68             sub parse_tag
69             {
70 21     21   32 my $self = shift;
71              
72 21         70 $self->expect( '<' );
73 21         76 my $tagname = $self->token_ident;
74              
75 11         39 my $attrs = $self->sequence_of( 'parse_tag_attr' );
76              
77 11         35 my $selfclose = $self->maybe_expect( '/' );
78 11         42 $self->expect( '>' );
79              
80             return {
81             name => $tagname,
82 11         59 attrs => { map { ( $_->[0], $_->[1] ) } @$attrs },
  2         13  
83             selfclose => $selfclose,
84             };
85             }
86              
87             sub parse_close_tag
88             {
89 10     10   16 my $self = shift;
90              
91 10         65 $self->expect( '
92 10         27 my $tagname = $self->token_ident;
93 10         34 $self->expect( '>' );
94              
95 10         56 return { name => $tagname };
96             }
97              
98             sub parse_tag_attr
99             {
100 13     13   23 my $self = shift;
101              
102 13         31 my $attrname = $self->token_ident;
103 2         9 $self->expect( '=' );
104 2         8 return [ $attrname => $self->parse_tag_attr_value ];
105             }
106              
107             sub parse_tag_attr_value
108             {
109 2     2   3 my $self = shift;
110              
111             # TODO: This sucks
112 2         16 return $self->token_string;
113             }
114              
115              
116 1     1   775 use Data::Dumper;
  1         10911  
  1         218  
117              
118             if( !caller ) {
119             my $parser = __PACKAGE__->new;
120              
121             my $ret = $parser->from_file( \*STDIN );
122             print Dumper( $ret );
123             }
124              
125              
126             package XmlParser::Node;
127 18     18   31 sub new { my $class = shift; bless [ @_ ], $class }
  18         168  
128              
129             package XmlParser::Node::Plain;
130 1     1   12 use base qw( XmlParser::Node );
  1         2  
  1         2143  
131 0     0   0 sub kind { "plain" }
132 0     0   0 sub text { shift->[0] }
133              
134             package XmlParser::Node::Element;
135 1     1   12 use base qw( XmlParser::Node );
  1         2  
  1         310  
136 5     5   20 sub kind { "element" }
137 5     5   40 sub name { shift->[0] }
138 0     0   0 sub attrs { shift->[1] }
139 5     5   10 sub children { my $self = shift; @{$self}[2..$#$self] }
  5         15  
  5         37  
140              
141             1;