File Coverage

blib/lib/YATT/Lite/LRXML/ParseBody.pm
Criterion Covered Total %
statement 121 131 92.3
branch 93 104 89.4
condition 59 75 78.6
subroutine 10 11 90.9
pod 0 4 0.0
total 283 325 87.0


line stmt bran cond sub pod time code
1             package YATT::Lite::LRXML::ParseBody; # dummy package, for lint.
2 16     16   6949 use strict;
  16         42  
  16         574  
3 16     16   84 use warnings qw(FATAL all NONFATAL misc);
  16         35  
  16         713  
4              
5 16     16   88 package YATT::Lite::LRXML; use YATT::Lite::LRXML;
  16         34  
  16         24995  
6              
7             sub _parse_body {
8 451     451   1292 (my MY $self, my Widget $widget, my ($sink, $close, $parent, $par_ln)) = @_;
9             # $sink は最初、外側の $body 配列。
10             # <:option /> が出現した所から先は、 その option element の body が新しい $sink になる
11              
12             # XXX: 使い方の指針を解説せよ
13             # curpos, startln, endln
14              
15 451         961 my $has_nonspace; # 非空白文字が出現したか。 <:opt>HEAD と BODY の間に
16             my $is_closed; # tag が閉じたか。
17              
18 451   100     6344 while (s{^(.*?)$$self{re_body}}{}xs or my $retry = $self->_get_chunk($sink)) {
19 1049 100       2836 next if $retry;
20              
21 713         2495 $self->accept_leading_text($sink, $parent, $par_ln, \$has_nonspace);
22              
23 713 100 66     7607 if ($+{lcmsg}) {
    100          
    100          
    50          
24 5 50       24 if ($+{msgopn}) {
25             push @$sink, $self->_parse_lcmsg
26 5         22 ($+{entity}, $parent, $par_ln, \$has_nonspace);
27             } else {
28             die $self->synerror_at
29 0         0 ($self->{startln}, q{Mismatched l10n msg});
30             }
31             } elsif ($+{entity} or $+{special}) {
32             # &yatt(?=:) までマッチしてる。
33             # XXX: space 許容モードも足すか。
34 340         1158 $self->accept_entity($sink, $parent, $par_ln, \$has_nonspace);
35              
36             } elsif (my $path = $+{elem}) {
37 325   50     2127 my $formal_path = ($+{opt} // '') . $+{elem};
38 325 100       1533 if ($+{clo}) {
39 71         176 $parent->[NODE_BODY_END] = $self->{startpos};
40 71 100 100     414 if (defined $parent->[NODE_BODY_BEGIN]
41             and $self->{template}->node_body_source($parent) =~ /(\r?\n)\Z/) {
42 52         149 $parent->[NODE_BODY_END] -= length $1;
43             }
44 71         296 $self->verify_tag($formal_path, $close);
45 66 100 100     691 if (@$sink and not ref $sink->[-1] and $sink->[-1] =~ s/(\r?\n)\Z//) {
      100        
46 49         148 push @$sink, "\n";
47             }
48             # $self->add_lineinfo($sink);
49 66         145 $is_closed++;
50 66         187 last;
51             }
52             # /? > まで、その後、not ee なら clo まで。
53 254         902 my $is_opt = $+{opt};
54             my $elem = [$is_opt ? TYPE_ATT_NESTED : TYPE_ELEMENT
55             , $self->{startpos}, undef, $self->{endln}
56 254 100       1595 , [split /:/, $path]
57             , undef];
58              
59 254 100       989 if (my @atts = $self->parse_attlist($_)) {
60 108         318 $elem->[NODE_ATTLIST] = \@atts;
61             }
62              
63             # タグの直後の改行は、独立したトークンにしておく
64             s{^(?/)? >(\r?\n)?}{}xs
65             or die $self->synerror_at($self->{startln}
66 254 50       1704 , q{Missing CLO(>) for: <%s, rest: '%s'}
67             , $path, trimmed($_));
68              
69             # body slot の初期化
70             # $is_opt の時に、更に body を attribute として保存するのは冗長だし、後の処理も手間なので
71 254         648 my $body = [];
72             $elem->[NODE_VALUE]
73             = $is_opt
74             ? $body : [TYPE_ATTRIBUTE, undef, undef, undef, body => $body]
75 254 100 100     1992 if not $+{empty_elem} or $is_opt;
    100          
76 254 100 100     1369 my $bodyStartRef = \ $elem->[NODE_BODY][NODE_LNO]
77             if not $is_opt and $elem->[NODE_VALUE];
78              
79 254 100       934 $self->{curpos} += 1 + ($1 ? length($1) : 0); # $& じゃないので注意。
80 254         572 $elem->[NODE_END] = $self->{curpos};
81 254 100       835 $self->{curpos} += length $2 if $2;
82 254         606 $elem->[NODE_BODY_BEGIN] = $self->{curpos};
83              
84 254 100       1006 $self->_verify_token($self->{curpos}, $_) if $self->{cf_debug};
85              
86 254 100 100     917 if ($is_opt and not $+{empty_elem}) {
87 10         45 drop_leading_ws($sink);
88             }
89              
90 254 100       851 if (not $is_opt) {
    100          
91 227         660 push @$sink, $elem;
92             } elsif ($+{empty_elem}) {
93             # <:opt/> の時は $parent->[foot] へ
94 17   100     39 push @{$parent->[NODE_AELEM_FOOT] ||= []}, $elem;
  17         97  
95             } else {
96             # <:opt> の時は, $parent->[head] へ
97 10   50     22 push @{$parent->[NODE_AELEM_HEAD] ||= []}, $elem
  10         66  
98             }
99              
100 254         539 my $bodystartln = $self->{endln};
101             # \n タグ直後の改行について。
102             # \n だけは, 現在の $sink へ、それ以外は、今作る $elem の $body へ改行を足す
103 254 100 100     783 $self->{endln}++, push @{!$is_opt && $+{empty_elem} ? $sink : $body}, "\n"
  98 100       635  
104             if $2;
105              
106 254 100 66     712 unless ($is_opt) {
107 227 100 100     1047 $$par_ln = $self->{startln} if not $has_nonspace++ and $parent;
108             } elsif (not $+{empty_elem}) {
109             # XXX: もし $is_opt かつ not ee だったら、
110             # $sink (親の $body) が空かどうかを調べる必要が有る。
111             # die $self->synerror_at(q{element option '%s' must precede body!}, $path)
112             # if $has_nonspace;
113             }
114 254 100       1203 if (not $+{empty_elem}) {
    100          
115             # call ... or complex option <:yatt:opt>
116             # expects or
117             # $self->{startln} = $self->{endln}; # No!
118             $self->_parse_body($widget, $body
119 73 50       481 , $+{empty_elem} ? $close : $formal_path
120             , $elem, $bodyStartRef);
121 66   66     298 $$bodyStartRef //= $bodystartln;
122             } elsif ($is_opt) {
123             # ee style option.
124             # <:yatt:foo/>bar 出現後は、以後の要素を att に加える。
125 17         39 $sink = $body;
126             } else {
127             } # simple call.
128 247 100       1030 $self->_verify_token($self->{curpos}, $_) if $self->{cf_debug};
129 247         842 $self->add_lineinfo($sink);
130              
131             } elsif ($path = $+{pi}) {
132 43 100 100     204 $$par_ln = $self->{startln} if not $has_nonspace++ and $parent;
133             # ?> まで
134 43 50       298 unless (s{^(.*?)\?>(\r?\n)?}{}s) {
135 0         0 die $self->synerror_at($self->{startln}, q{Unbalanced pi});
136             }
137 43         159 my $end = $self->{curpos} += 2 + length($1);
138 43 100       132 my $nl = "\n" if $2;
139             # XXX: parse_text の前なので、本当は良くない
140 43 100       149 $self->{curpos} += length $2 if $2;
141             push @$sink, [TYPE_PI, $self->{startpos}, $end
142             , $self->{endln}
143 43         288 , [split /:/, $path]
144             , lexpand($self->_parse_text_entities($1))];
145 43 100       142 if ($nl) {
146 6         19 push @$sink, $nl;
147 6         22 $self->{startln} = ++$self->{endln};
148             }
149 43         144 $self->add_lineinfo($sink);
150             } else {
151 0         0 die join("", "Can't parse: ", nonmatched($_));
152             }
153             } continue {
154 970         1975 $self->{startln} = $self->{endln};
155 970         1682 $self->{startpos} = $self->{curpos};
156 970 100       6020 $self->_verify_token($self->{startpos}, $_) if $self->{cf_debug};
157             }
158              
159 438 100 100     1555 if ($close and not $is_closed) {
160 1         5 die $self->synerror_at($self->{startln}, q{Missing close tag '%s'}, $close);
161             }
162              
163             # To make body-less element easily detected.
164 437 50 66     1545 if ($parent and $parent->[NODE_VALUE]) {
165 66         323 _undef_if_empty($self->node_body_slot($parent));
166             }
167             }
168              
169             sub accept_leading_text {
170 730     730 0 1725 (my MY $self, my ($sink, $parent, $par_ln, $rhas_nonspace)) = @_;
171 730         2451 $self->{endln} += numLines($&);
172 730 100       2730 if ($self->add_posinfo(length($1), 1)) {
173 450         1369 push @$sink, splitline($1);
174             $$par_ln = $self->{startln}
175 450 100 100     1353 if nonspace($1) and not $$rhas_nonspace++ and $parent;
      100        
176 450         1494 $self->{startln} += numLines($1);
177             }
178 730         1863 $self->{curpos} += length($&) - length($1);
179 730 100       2521 $self->_verify_token($self->{curpos}, $_) if $self->{cf_debug};
180             }
181              
182             sub accept_entity {
183 350     350 0 884 (my MY $self, my ($sink, $parent, $par_ln, $rhas_nonspace)) = @_;
184             push @$sink, my $node = $self->mkentity
185 350         1387 ($self->{startpos}, undef, $self->{endln});
186             # ; まで
187 349         849 $node->[NODE_END] = $self->{curpos};
188 349 100       2067 $self->_verify_token($self->{curpos}, $_) if $self->{cf_debug};
189 349         1264 $self->add_lineinfo($sink);
190             $$par_ln = $self->{startln}
191 349 50 66     1034 if nonspace($1) and not $$rhas_nonspace++ and $parent;
      33        
192             }
193              
194             sub verify_tag {
195 71     71 0 196 (my MY $self, my ($path, $close)) = @_;
196             # XXX: デバッグ時、この段階での sink の様子を見たくなる。
197 71 100       348 unless (s{^>}{}xs) {
198 1         6 die $self->synerror_at($self->{endln}, q{Missing CLO(>) for: <%s}, $path);
199             }
200 70         192 $self->{curpos} += 1;
201 70 100 66     324 unless (defined $close) {
202 1         6 die $self->synerror_at($self->{endln}, q{TAG close without open! got }, $path);
203             } elsif ($path ne $close) {
204             die $self->synerror_at($self->{endln}, q{TAG Mismatch! <%s> closed by }
205             , $close, $path);
206             }
207             }
208              
209             # $_ から &yatt]]; までを削って $node を返す
210              
211             sub _parse_lcmsg {
212 5     5   23 (my MY $self, my ($ns, $parent, $par_ln, $rhas_nonspace)) = @_;
213              
214 5         12 my $path = [$ns];
215 5 50       32 if (s/^(?:\#(\w+))?\[{2,};//) {
216 5 100       26 push @$path, $1 if $1;
217             } else {
218             die $self->synerror_at
219             ($self->{startln}
220 0         0 , q{parse_lcmsg is called from invalid context: %s }, $_);
221             }
222              
223              
224             my $node = [TYPE_LCMSG, $self->{startpos}, undef, $self->{endln}
225 5         20 , $path
226             , my $body = [my $sink = []]];
227              
228 5         12 $self->{curpos} += length $&;
229              
230 5   33     148 while (length $_ and s{^(.*?)$$self{re_entopn}}{}s) {
231 17         64 $self->accept_leading_text($sink, $parent, $par_ln, $rhas_nonspace);
232 17 50 33     159 if ($+{msgopn}) {
    100          
    100          
    50          
233             die $self->synerror_at
234 0         0 ($self->{startln}, q{nesting of l10n msg is not allowed});
235             } elsif ($+{msgsep}) {
236 2         12 s/^\|{2,};//;
237 2         8 $self->{curpos} += length $&;
238             # switch to next sink.
239 2         20 push @$body, $sink = [];
240              
241             } elsif ($+{msgclo}) {
242 5         26 s/^\]{2,};//;
243 5         15 $self->{curpos} += length $&;
244 5         13 $node->[NODE_END] = $self->{curpos};
245 5         21 return $node;
246              
247             } elsif ($+{entity} or $+{special}) {
248 10         31 $self->accept_entity($sink, $parent, $par_ln, $rhas_nonspace);
249             } else {
250             die $self->synerror_at
251 0         0 ($self->{startln}, q{Unknown input: %s}, $_);
252             }
253             }
254              
255             die $self->synerror_at
256             ($self->{startln}
257 0         0 , q{parse_lcmsg is not closed: %s}, $_);
258             }
259              
260             sub _undef_if_empty {
261 66 50 33 66   425 return unless defined $_[0] and ref $_[0] eq 'ARRAY';
262 66 100       134 unless (@{$_[0]}) {
  66         241  
263 1         4 undef $_[0];
264             }
265             }
266              
267             sub trimmed {
268 0     0 0   my ($str) = @_;
269 0           $str =~ s/\n.*\z//s;
270 0           $str;
271             }
272              
273 16     16   144 use YATT::Lite::Breakpoint qw(break_load_parsebody);
  16         42  
  16         855  
274             break_load_parsebody();
275              
276             1;