File Coverage

blib/lib/YATT/Lite/LRXML/ParseBody.pm
Criterion Covered Total %
statement 122 132 92.4
branch 97 108 89.8
condition 53 69 76.8
subroutine 10 11 90.9
pod 0 4 0.0
total 282 324 87.0


line stmt bran cond sub pod time code
1             package YATT::Lite::LRXML::ParseBody; # dummy package, for lint.
2 10     10   6595 use strict;
  10         21  
  10         326  
3 10     10   53 use warnings qw(FATAL all NONFATAL misc);
  10         29  
  10         555  
4              
5 10     10   50 package YATT::Lite::LRXML; use YATT::Lite::LRXML;
  10         22  
  10         23085  
6              
7             sub _parse_body {
8 329     329   793 (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 329         524 my $has_nonspace; # 非空白文字が出現したか。 <:opt>HEAD と BODY の間に
16             my $is_closed; # tag が閉じたか。
17              
18 329   100     4473 while (s{^(.*?)$$self{re_body}}{}xs or my $retry = $self->_get_chunk($sink)) {
19 764 100       1676 next if $retry;
20              
21 518         1385 $self->accept_leading_text($sink, $parent, $par_ln, \$has_nonspace);
22              
23 518 100 66     7067 if ($+{lcmsg}) {
    100          
    100          
    50          
24 5 50       29 if ($+{msgopn}) {
25             push @$sink, $self->_parse_lcmsg
26 5         23 ($+{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 212         634 $self->accept_entity($sink, $parent, $par_ln, \$has_nonspace);
35              
36             } elsif (my $path = $+{elem}) {
37 258   50     2109 my $formal_path = ($+{opt} // '') . $+{elem};
38 258 100       1489 if ($+{clo}) {
39 69         190 $parent->[NODE_BODY_END] = $self->{startpos};
40 69 100 100     446 if (defined $parent->[NODE_BODY_BEGIN]
41             and $self->{template}->node_body_source($parent) =~ /(\r?\n)\Z/) {
42 51         114 $parent->[NODE_BODY_END] -= length $1;
43             }
44 69         245 $self->verify_tag($formal_path, $close);
45 64 100 100     697 if (@$sink and not ref $sink->[-1] and $sink->[-1] =~ s/(\r?\n)\Z//) {
      100        
46 48         103 push @$sink, "\n";
47             }
48             # $self->add_lineinfo($sink);
49 64         114 $is_closed++;
50 64         156 last;
51             }
52             # /? > まで、その後、not ee なら clo まで。
53 189         831 my $is_opt = $+{opt};
54             my $elem = [$is_opt ? TYPE_ATT_NESTED : TYPE_ELEMENT
55             , $self->{startpos}, undef, $self->{endln}
56 189 100       1306 , [split /:/, $path]
57             , undef];
58              
59 189 100       745 if (my @atts = $self->parse_attlist($_)) {
60 101         248 $elem->[NODE_ATTLIST] = \@atts;
61             }
62              
63             # タグの直後の改行は、独立したトークンにしておく
64             s{^(?/)? >(\r?\n)?}{}xs
65             or die $self->synerror_at($self->{startln}
66 189 50       1203 , q{Missing CLO(>) for: <%s, rest: '%s'}
67             , $path, trimmed($_));
68              
69             # body slot の初期化
70             # $is_opt の時に、更に body を attribute として保存するのは冗長だし、後の処理も手間なので
71 189         358 my $body = [];
72             $elem->[NODE_VALUE]
73             = $is_opt
74             ? $body : [TYPE_ATTRIBUTE, undef, undef, undef, body => $body]
75 189 100 100     1604 if not $+{empty_elem} or $is_opt;
    100          
76 189 100 66     886 my $bodyStartRef = \ $elem->[NODE_BODY][NODE_LNO]
77             if not $is_opt and $elem->[NODE_VALUE];
78              
79 189 100       641 $self->{curpos} += 1 + ($1 ? length($1) : 0); # $& じゃないので注意。
80 189         326 $elem->[NODE_END] = $self->{curpos};
81 189 100       586 $self->{curpos} += length $2 if $2;
82 189         414 $elem->[NODE_BODY_BEGIN] = $self->{curpos};
83              
84 189 100       777 $self->_verify_token($self->{curpos}, $_) if $self->{cf_debug};
85              
86 189 100 100     679 if ($is_opt and not $+{empty_elem}) {
87 10         34 drop_leading_ws($sink);
88             }
89              
90 189 100       485 if (not $is_opt) {
    100          
91 162         331 push @$sink, $elem;
92             } elsif ($+{empty_elem}) {
93             # <:opt/> の時は $parent->[foot] へ
94 17   100     31 push @{$parent->[NODE_AELEM_FOOT] ||= []}, $elem;
  17         105  
95             } else {
96             # <:opt> の時は, $parent->[head] へ
97 10   50     17 push @{$parent->[NODE_AELEM_HEAD] ||= []}, $elem
  10         61  
98             }
99              
100 189         349 my $bodystartln = $self->{endln};
101             # \n タグ直後の改行について。
102             # \n だけは, 現在の $sink へ、それ以外は、今作る $elem の $body へ改行を足す
103 189 100 66     569 $self->{endln}++, push @{!$is_opt && $+{empty_elem} ? $sink : $body}, "\n"
  94 100       628  
104             if $2;
105              
106 189 100       574 unless ($is_opt) {
    100          
107 162 100 100     715 $$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 189 100       1071 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 71 50       474 , $+{empty_elem} ? $close : $formal_path
120             , $elem, $bodyStartRef);
121 64   66     224 $$bodyStartRef //= $bodystartln;
122             } elsif ($is_opt) {
123             # ee style option.
124             # <:yatt:foo/>bar 出現後は、以後の要素を att に加える。
125 17         30 $sink = $body;
126             } else {
127             } # simple call.
128 182 100       876 $self->_verify_token($self->{curpos}, $_) if $self->{cf_debug};
129 182         525 $self->add_lineinfo($sink);
130              
131             } elsif ($path = $+{pi}) {
132 43 100 100     189 $$par_ln = $self->{startln} if not $has_nonspace++ and $parent;
133             # ?> まで
134 43 50       300 unless (s{^(.*?)\?>(\r?\n)?}{}s) {
135 0         0 die $self->synerror_at($self->{startln}, q{Unbalanced pi});
136             }
137 43         133 my $end = $self->{curpos} += 2 + length($1);
138 43 100       126 my $nl = "\n" if $2;
139             # XXX: parse_text の前なので、本当は良くない
140 43 100       109 $self->{curpos} += length $2 if $2;
141             push @$sink, [TYPE_PI, $self->{startpos}, $end
142             , $self->{endln}
143 43         301 , [split /:/, $path]
144             , lexpand($self->_parse_text_entities($1))];
145 43 100       136 if ($nl) {
146 6         16 push @$sink, $nl;
147 6         16 $self->{startln} = ++$self->{endln};
148             }
149 43         132 $self->add_lineinfo($sink);
150             } else {
151 0         0 die join("", "Can't parse: ", nonmatched($_));
152             }
153             } continue {
154 687         1198 $self->{startln} = $self->{endln};
155 687         1088 $self->{startpos} = $self->{curpos};
156 687 100       4145 $self->_verify_token($self->{startpos}, $_) if $self->{cf_debug};
157             }
158              
159 316 100 100     1115 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 315 50 66     1137 if ($parent and $parent->[NODE_VALUE]) {
165 64         292 _undef_if_empty($self->node_body_slot($parent));
166             }
167             }
168              
169             sub accept_leading_text {
170 535     535 0 1108 (my MY $self, my ($sink, $parent, $par_ln, $rhas_nonspace)) = @_;
171 535         1821 $self->{endln} += numLines($&);
172 535 100       2086 if ($self->add_posinfo(length($1), 1)) {
173 335         939 push @$sink, splitline($1);
174             $$par_ln = $self->{startln}
175 335 100 100     948 if nonspace($1) and not $$rhas_nonspace++ and $parent;
      100        
176 335         1046 $self->{startln} += numLines($1);
177             }
178 535         1415 $self->{curpos} += length($&) - length($1);
179 535 100       2081 $self->_verify_token($self->{curpos}, $_) if $self->{cf_debug};
180             }
181              
182             sub accept_entity {
183 222     222 0 511 (my MY $self, my ($sink, $parent, $par_ln, $rhas_nonspace)) = @_;
184             push @$sink, my $node = $self->mkentity
185 222         905 ($self->{startpos}, undef, $self->{endln});
186             # ; まで
187 221         611 $node->[NODE_END] = $self->{curpos};
188 221 100       968 $self->_verify_token($self->{curpos}, $_) if $self->{cf_debug};
189 221         662 $self->add_lineinfo($sink);
190             $$par_ln = $self->{startln}
191 221 50 66     560 if nonspace($1) and not $$rhas_nonspace++ and $parent;
      33        
192             }
193              
194             sub verify_tag {
195 69     69 0 167 (my MY $self, my ($path, $close)) = @_;
196             # XXX: デバッグ時、この段階での sink の様子を見たくなる。
197 69 100       347 unless (s{^>}{}xs) {
198 1         6 die $self->synerror_at($self->{endln}, q{Missing CLO(>) for: <%s}, $path);
199             }
200 68         141 $self->{curpos} += 1;
201 68 100       307 unless (defined $close) {
    100          
202 1         5 die $self->synerror_at($self->{endln}, q{TAG close without open! got }, $path);
203             } elsif ($path ne $close) {
204 3         14 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   25 (my MY $self, my ($ns, $parent, $par_ln, $rhas_nonspace)) = @_;
213              
214 5         12 my $path = [$ns];
215 5 50       33 if (s/^(?:\#(\w+))?\[{2,};//) {
216 5 100       20 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         21 , $path
226             , my $body = [my $sink = []]];
227              
228 5         13 $self->{curpos} += length $&;
229              
230 5   33     174 while (length $_ and s{^(.*?)$$self{re_entopn}}{}s) {
231 17         44 $self->accept_leading_text($sink, $parent, $par_ln, $rhas_nonspace);
232 17 50 33     221 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         10 s/^\|{2,};//;
237 2         6 $self->{curpos} += length $&;
238             # switch to next sink.
239 2         26 push @$body, $sink = [];
240              
241             } elsif ($+{msgclo}) {
242 5         21 s/^\]{2,};//;
243 5         13 $self->{curpos} += length $&;
244 5         9 $node->[NODE_END] = $self->{curpos};
245 5         20 return $node;
246              
247             } elsif ($+{entity} or $+{special}) {
248 10         27 $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 64 50 33 64   406 return unless defined $_[0] and ref $_[0] eq 'ARRAY';
262 64 100       100 unless (@{$_[0]}) {
  64         252  
263 1         3 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 10     10   72 use YATT::Lite::Breakpoint qw(break_load_parsebody);
  10         17  
  10         665  
274             break_load_parsebody();
275              
276             1;