File Coverage

blib/lib/Parse/KeyValue/Shellish/Parser.pm
Criterion Covered Total %
statement 100 100 100.0
branch 44 46 95.6
condition 19 21 90.4
subroutine 7 7 100.0
pod 0 2 0.0
total 170 176 96.5


line stmt bran cond sub pod time code
1             package Parse::KeyValue::Shellish::Parser;
2 2     2   8 use strict;
  2         3  
  2         56  
3 2     2   9 use warnings;
  2         4  
  2         45  
4 2     2   9 use Carp;
  2         5  
  2         2042  
5              
6             sub new {
7 14     14 0 27 my ($class, $str) = @_;
8              
9 14         154 bless {
10             index => 0,
11             escaped => 0,
12             key => '',
13             str => $str,
14             strlen => length $str,
15             parsed => {},
16             }, $class;
17             }
18              
19             sub parse {
20 14     14 0 22 my ($self) = @_;
21              
22 14         69 my $value = '';
23 14         36 my $strlen = $self->{strlen};
24 14         59 for ($self->{index} = 0; $self->{index} < $strlen; $self->{index}++) {
25 210         485 my $ch = substr($self->{str}, $self->{index}, 1);
26              
27 210 100       458 if ($ch eq '=') {
28 30 100       297 if ($self->{key}) { # for example `foo=bar=buz`
29 1         3 $value .= $ch;
30 1         4 next;
31             }
32 29         47 $self->{key} = $value;
33 29         31 $value = '';
34 29         82 next;
35             }
36              
37 180 100       415 if ($ch =~ /\s/) {
38 18 100       42 if ($self->{escaped}) {
39 3         4 $value .= $ch;
40 3         5 $self->{escaped} = 0;
41 3         11 next;
42             }
43              
44 15 100       32 if ($self->{key}) {
45 13         43 $self->{parsed}->{$self->{key}} = $value;
46             }
47 15         20 $self->{key} = '';
48 15         20 $value = '';
49 15         43 next;
50             }
51              
52 162 100 100     798 if ($ch eq "'" || $ch eq '"') {
53 11         83 $value .= $self->_parse_in_quote($ch);
54 8         27 next;
55             }
56              
57 151 100 100     426 if ($ch eq '(' && !$self->{escaped}) {
58 6         20 $self->_parse_in_paren;
59 4         12 next;
60             }
61              
62 145 100 100     307 if ($ch eq ')' && !$self->{escaped}) {
63 2         523 croak qq{[ERROR] Unbalanced parenthesis "$self->{str}"};
64             }
65              
66 143 100       249 if ($ch eq '\\') {
67 9 100       686 if ($self->{escaped}) {
68 2         3 $value .= $ch;
69 2         3 $self->{escaped} = 0;
70 2         8 next;
71             }
72 7         10 $self->{escaped} = 1;
73 7         20 next;
74             }
75              
76 134         150 $value .= $ch;
77 134         482 $self->{escaped} = 0;
78             }
79              
80 7 100 66     47 if ($self->{key} && $value) {
81 6 50       15 $value .= '\\' if $self->{escaped};
82 6         45 $self->{parsed}->{$self->{key}} = $value;
83             }
84              
85 7         102 return $self->{parsed};
86             }
87              
88             sub _parse_in_quote {
89 14     14   23 my ($self, $quote) = @_;
90              
91 14         17 my $balanced = 0;
92 14         19 my $value = '';
93 14         25 my $strlen = $self->{strlen};
94              
95 14         45 for ($self->{index}++; $self->{index} < $strlen; $self->{index}++) {
96 59         94 my $ch = substr $self->{str}, $self->{index}, 1;
97              
98 59 100 100     365 if ($ch eq $quote && !$self->{escaped}) {
99 11         12 $balanced = 1;
100 11         97 last;
101             }
102              
103 48 100       79 if ($ch eq '\\') {
104 3 100       13 if ($quote eq "'") {
105 2         3 $value .= $ch;
106 2         6 next;
107             }
108 1         3 $self->{escaped} = 1;
109 1         3 next;
110             }
111              
112 45         46 $value .= $ch;
113 45         121 $self->{escaped} = 0;
114             }
115              
116 14 100       668 croak qq{[ERROR] Unbalanced quotation: "$self->{str}"} unless $balanced;
117              
118 11         25 return $value;
119             }
120              
121             sub _parse_in_paren {
122 6     6   12 my ($self) = @_;
123              
124 6         6 my @array;
125 6         7 my $balanced = 0;
126 6         12 my $value = '';
127 6         9 my $strlen = $self->{strlen};
128              
129 6         20 for ($self->{index}++; $self->{index} < $strlen; $self->{index}++) {
130 22         43 my $ch = substr($self->{str}, $self->{index}, 1);
131              
132 22 100       44 if ($ch eq '(') {
133 1         149 croak qq{[ERROR] Unbalanced parenthesis "$self->{str}"};
134             }
135              
136 21 100       40 if ($ch eq ')') {
137 4         4 $balanced = 1;
138 4         7 last;
139             }
140              
141 17 100       40 if ($ch =~ /\s/) {
142 4         8 push @array, $value;
143 4         4 $value = '';
144 4         11 next;
145             }
146              
147 13 100 100     124 if ($ch eq "'" || $ch eq '"') {
148 3         9 $value .= $self->_parse_in_quote($ch);
149 3         10 next;
150             }
151              
152 10         29 $value .= $ch;
153             }
154              
155 5 100 66     25 if ($self->{key} && $value) {
156 3 50       8 $value .= '\\' if $self->{escaped};
157 3         6 push @array, $value;
158 3         5 $value = '';
159             }
160              
161 5 100       173 croak qq{[ERROR] Unbalanced parenthesis "$self->{str}"} unless $balanced;
162              
163 4         99 $self->{parsed}->{$self->{key}} = \@array;
164 4         10 $self->{key} = '';
165             }
166              
167             1;
168