File Coverage

blib/lib/YATT/Lite/LRXML.pm
Criterion Covered Total %
statement 425 474 89.6
branch 156 220 70.9
condition 65 106 61.3
subroutine 65 70 92.8
pod 1 50 2.0
total 712 920 77.3


line stmt bran cond sub pod time code
1             #========================================
2             # Parsing and Building. part の型を確定させる所まで請け負うことに。
3             package YATT::Lite::LRXML; sub MY () {__PACKAGE__}
4 17     17   1050075 use strict;
  17         49  
  17         698  
5 17     17   100 use warnings qw(FATAL all NONFATAL misc);
  17         40  
  17         622  
6 17     17   375 use 5.010; no if $] >= 5.017011, warnings => "experimental";
  17     17   64  
  17         351  
  17         5880  
  17         140  
7              
8 17     17   1275 use base qw(YATT::Lite::VarMaker);
  17         46  
  17         7384  
9 17         150 use fields qw/re_decl
10             re_body
11             re_entopn
12             re_att
13             re_name
14             re_evar ch_etext
15             re_eparen
16             re_eopen re_eclose
17              
18             template
19             chunklist
20             startln endln
21             startpos curpos
22             cf_namespace
23             cf_vfs
24             cf_default_part
25             cf_base cf_scheme cf_path cf_encoding cf_debug
26             cf_all
27             cf_special_entities
28             subroutes
29             rootroute
30              
31             cf_match_argsroute_first
32              
33             _original_entpath
34 17     17   122 /;
  17         39  
35              
36 17     17   10251 use YATT::Lite::Core qw(Part Widget Page Action Data Template);
  17         53  
  17         1515  
37 17     17   125 use YATT::Lite::VarTypes;
  17         40  
  17         614  
38 17     17   95 use YATT::Lite::Constants;
  17         37  
  17         2346  
39 17     17   113 use YATT::Lite::Util qw(numLines default untaint_unless_tainted lexpand);
  17         38  
  17         788  
40              
41 17     17   7541 use YATT::Lite::RegexpNames;
  17         45  
  17         1049  
42              
43             require Scalar::Util;
44             require Encode;
45 17     17   111 use Carp;
  17         39  
  17         31482  
46              
47             #========================================
48 230     230 0 962 sub default_public_part {'page'}
49 16     16 0 58 sub default_private_part {'widget'}
50             sub default_part_for {
51 246     246 0 573 (my MY $self, my Template $tmpl) = @_;
52             $tmpl->{cf_public}
53 246 100       1031 ? $self->default_public_part
54             : $self->default_private_part;
55             }
56              
57             #========================================
58             sub after_new {
59 457     457 1 834 my MY $self = shift;
60 457         1901 $self->SUPER::after_new;
61 457 100       2331 Scalar::Util::weaken($self->{cf_vfs}) if $self->{cf_vfs};
62 457   100     2770 $self->{cf_namespace} ||= [qw(yatt perl)];
63 457         767 my $nspat = qr!@{[join "|", $self->namespace]}!;
  457         1470  
64 457   33     3118 $self->{re_name} ||= $self->re_name;
65 457   33     4936 $self->{re_decl} ||= qr{$nspat(?::\w++)+)
66             |(?:--\#(?$nspat(?::\w++)*)))\b}xs;
67 457         850 my $entOpen = do {
68             # qq なので注意
69 457         1152 my $entbase = qq{(?$nspat)};
70 457         931 $entbase .= sprintf(q{(?=%s)}, join "|"
71             , ':'
72             , sprintf(q{(?%s)}, join "|"
73             , q{(?(?:\#\w+)?\[{2,})}
74             , q{(?\|{2,})}
75             , q{(?\]{2,})}));
76 457         1038 my @entPat = $entbase;
77             # special の場合は entgroup を呼びたいので、 先に open ( を削っておく。
78             push @entPat, sprintf q{(?(?:%s))\(}
79             , join "|", lexpand($self->{cf_special_entities})
80 457 100       1365 if $self->{cf_special_entities};
81 457         2021 sprintf q{&(?:%s)}, join "|", @entPat;
82             };
83             $self->{re_att}
84 457   33     7124 ||= qr{(?\s++)
85             | (?--+.*?--+)
86             | (?%(?:[\w\:\.]+(?:[\w:\.\-=\[\]\{\}\(,\)]+)?);)
87             | (?:(?[\w:]+)\s*=\s*+)?+
88             (?:'(?[^']*+)'
89             |"(?[^\"]*+)"
90             |(?\[) | (?\])
91             |$entOpen
92             |(?[^\s'\"<>\[\]/=]++)
93             )
94             }xs;
95 457   33     4546 $self->{re_body} ||= qr{$entOpen
96             |<(?:(?/?)(?:?)(?$nspat(?::\w++)+)
97             |\?(?$nspat(?::\w++)*))\b
98             }xs;
99             # For entities.
100 457         1806 $self->{re_entopn} = qr{$entOpen}xs;
101 457   33     2804 $self->{re_eopen} ||= qr{(? [\(\{\[])}xs;
102 457   33     2287 $self->{re_eclose} ||= qr{(? [\)\}\]])}xs;
103 457   33     2257 $self->{re_evar} ||= qr{: (?\w+)}xs;
104 457   33     2353 $self->{ch_etext} ||= qr{(?: [^\ \t\n,;:()\[\]{}])}xs;
105 457   33     2592 $self->{re_eparen} ||= qr{(\( (? (?: (?> [^()]+) | (?-2) )*) \) )}xs;
106 457         1555 $self;
107             }
108             #========================================
109              
110             # Debugging aid.
111             # YATT::Lite::LRXML->load_from(string => '...template...')
112             #
113             sub load_from {
114 0     0 0 0 my ($pack, $loadSpec, $tmplSpec, @moreLoadArgs) = @_;
115              
116 0 0       0 my ($loadType, @loadArgs) = ref $loadSpec ? @$loadSpec : $loadSpec;
117 0 0       0 unless (defined $loadType) {
118 0         0 croak "Undefined source type";
119             }
120 0 0       0 my $sub = $pack->can("load_${loadType}_into")
121             or croak "Unknown source type: $loadType";
122              
123 0 0       0 my ($tmplFrom, @tmplArgs) = ref $tmplSpec ? @$tmplSpec : $tmplSpec;
124 0         0 my Template $tmpl = $pack->Template->new(@tmplArgs);
125              
126             # デフォルトでは body もパースする.
127             # XXX: オプション名 all だと分かりにくい。公式にする前に、改名すべき。
128 0         0 $sub->($pack, $tmpl, $tmplFrom, all => 1, @loadArgs, @moreLoadArgs);
129             }
130              
131             sub load_file_into {
132 79     79 0 271 my ($pack, $tmpl, $fn) = splice @_, 0, 3;
133 79 50 33     686 croak "Template argument is missing!
134             YATT::Lite::Parser->from_file(filename, templateObject)"
135             unless defined $tmpl and UNIVERSAL::isa($tmpl, $pack->Template);
136 79 50       261 unless (defined $fn) {
137 0         0 croak "filename is undef!";
138             }
139 79 50       370 my MY $self = ref $pack ? $pack->configure(@_) : $pack->new(@_);
140 79 50       3208 open my $fh, '<', $fn or die "Can't open $fn: $!";
141 79 50       1021 binmode $fh, ":encoding($$self{cf_encoding})" if $$self{cf_encoding};
142 79         5401 $self->{cf_path} = $fn;
143 79         177 $self->{cf_scheme} = 'file';
144 79         146 my $string = do {
145 79         316 local $/;
146 79         2185 untaint_unless_tainted($fn, scalar <$fh>);
147             };
148 79         325 $self->load_string_into($tmpl, $string);
149             }
150              
151             sub load_string_into {
152 244     244 0 879 (my $pack, my Template $tmpl) = splice @_, 0, 2;
153 244 100       1364 my MY $self = ref $pack ? $pack->configure(@_[1 .. $#_])
154             : $pack->new(@_[1 .. $#_]);
155 244 50       717 unless (defined $_[0]) {
156 0         0 croak "template string is undef!";
157             }
158 244         1060 $self->parse_decl($tmpl, $_[0]);
159 241 100       680 $self->parse_body($tmpl) if $self->{cf_all};
160 241 50       5565 wantarray ? ($tmpl, $self) : $tmpl;
161             }
162              
163             sub parse_body {
164 236     236 0 621 (my MY $self, my Template $tmpl) = @_;
165 236 100       698 return if $tmpl->{parse_ok};
166 234         521 $self->{template} = $tmpl;
167 234         1321 $self->parse_widget($_) for $tmpl->list_parts($self->Widget);
168 227         896 $tmpl->{parse_ok} = 1;
169             }
170              
171             sub posinfo {
172 10     10 0 18 (my MY $self) = shift;
173 10         34 ($self->{startpos}, $self->{curpos});
174             }
175              
176             sub add_posinfo {
177 1138     1138 0 2346 (my MY $self, my ($len, $sync)) = @_;
178 1138         2046 $self->{curpos} += $len;
179 1138 100       2817 $self->{startpos} = $self->{curpos} if $sync;
180 1138         2847 $len;
181             }
182              
183             sub update_posinfo {
184 0     0 0 0 my MY $self = shift;
185 0         0 my ($sync) = splice @_, 1;
186             # $self->{curpos} = $self->{total} - length $_[0];
187 0 0       0 $self->{startpos} = $self->{curpos} if $sync;
188             }
189              
190             sub parse_decl {
191 244     244 0 739 (my MY $self, my Template $tmpl, my $str, my @config) = @_;
192 244         1303 break_parser();
193 244         530 $self->{template} = $tmpl;
194 244 100       824 $tmpl->reset if $tmpl->{product};
195 244         841 $self->configure(@config);
196 244         496 $tmpl->{cf_string} = $str;
197 244         959 $tmpl->{cf_utf8} = Encode::is_utf8($str);
198 244         565 $self->{startln} = $self->{endln} = 1;
199 244         932 $self->add_part($tmpl, my Part $part = $self->build
200             ($self->primary_ns, $self->default_part_for($tmpl)
201             , '', implicit => 1
202             , startpos => 0, bodypos => 0));
203 244         1140 ($self->{startpos}, $self->{curpos}, my $total) = (0, 0, length $str);
204 244         3399 while ($str =~ s{^(.*?)($$self{re_decl})}{}s) {
205 265 100       1343 $self->add_text($part, $1) if length $1;
206 265         731 $self->{curpos} = $total - length $str;
207 265 100   15   2075 if (my $comment_ns = $+{comment}) {
  15         520  
  15         11599  
  15         58097  
208 10 50       53 unless ($str =~ s{^(.*?)-->(\r?\n)?}{}s) {
209 0         0 die $self->synerror_at($self->{startln}, q{Comment is not closed});
210             }
211 10 100       33 my $nlines = numLines($1) + ($2 ? 1 : 0);
212 10         22 $self->{curpos} += length $&;
213 10         25 push @{$part->{toks}}, [TYPE_COMMENT, $self->posinfo($str)
214             , $self->{startln}
215 10         16 , $comment_ns, $nlines, $1];
216 10         19 $self->{startln} = $self->{endln} += $nlines;
217 10         18 next;
218             }
219 255         1638 my ($ns, $kind) = split /:/, $+{declname}, 2;
220             # XXX: build と declare の順序が逆ではないか? 気にしなくていい?
221 255         642 my $is_new;
222 255 100       1661 if ($self->can("build_$kind")) {
    100          
223             # yatt:widget, action
224 149         507 my (@args) = $self->parse_attlist($str, 1); # To delay entity parsing.
225 149 50       729 my $nameAtt = YATT::Lite::Constants::cut_first_att(\@args) or do {
226 0         0 die $self->synerror_at($self->{startln}, q{No part name in %s:%s\n%s}
227             , $ns, $kind
228             , nonmatched($str));
229             };
230 149         576 my ($partName, $mapping, @opts);
231 149 100       434 if ($nameAtt->[NODE_TYPE] == TYPE_ATT_NAMEONLY) {
    50          
232 114         248 $partName = $nameAtt->[NODE_PATH];
233             } elsif ($nameAtt->[NODE_TYPE] == TYPE_ATT_TEXT) {
234             # $partName が foo=bar なら pattern として扱う
235             $mapping = $self->parse_location
236 35 50       143 ($nameAtt->[NODE_BODY], $nameAtt->[NODE_PATH]) or do {
237             die $self->synerror_at($self->{startln}
238 0         0 , q{Invalid location in %s:%s - "%s"}
239             , $ns, $kind, $nameAtt->[NODE_BODY])
240             };
241 35   66     214 $partName = $nameAtt->[NODE_PATH]
242             // $self->location2name($nameAtt->[NODE_BODY]);
243             } else {
244 0         0 die $self->synerror_at($self->{startln}, q{Invalid part name in %s:%s}
245             , $ns, $kind);
246             }
247 149         471 $self->add_part($tmpl, $part = $self->build($ns, $kind, $partName));
248 149 100       407 if ($mapping) {
249 35         129 $mapping->configure(item => $part);
250 35         160 $self->{subroutes}->append($mapping);
251 35         129 $self->add_url_params($part, lexpand($mapping->cget('params')));
252             }
253 149         561 $self->add_args($part, @args);
254 149         446 $is_new++;
255             } elsif (my $sub = $self->can("declare_$kind")) {
256             # yatt:base, yatt:args vs perl:base, perl:args...
257             # 戻り値が undef なら、同じ $part を用いつづける。
258 104   66     502 $part = $sub->($self, $tmpl, $ns, $self->parse_attlist($str, 1))
259             // $part;
260             } else {
261 2         13 die $self->synerror_at($self->{startln}, q{Unknown declarator ()}, $ns, $kind);
262             }
263 252 50       2137 unless ($str =~ s{^>([\ \t]*\r?\n)?}{}s) {
264             # XXX: たくさん出しすぎ
265 0         0 die $self->synerror_at($self->{startln}, q{Invalid character in decl %s:%s : %s}
266             , $ns, $kind
267             , $str);
268             }
269             # の直後には改行が必要、とする。
270 252 50       1010 unless ($1) {
271 0         0 die $self->synerror_at($self->{startln}, q{ must end with newline!}, $ns, $kind);
272             }
273 252         1058 $self->add_posinfo(length $&);
274 252         929 $self->{endln} += numLines($1);
275 252         574 $part->{cf_bodypos} = $self->{curpos};
276 252         573 $part->{cf_bodyln} = $self->{endln}; # part の本体開始行の初期値
277             } continue {
278 262         2131 $self->{startpos} = $self->{curpos};
279             }
280 241         510 push @{$part->{toks}}, nonmatched($str);
  241         820  
281             # widget->{cf_endln} は, (視覚上の最後の行)より一つ先の行を指す。(末尾の改行を数える分,多い)
282 241         866 $part->{cf_endln} = $self->{endln} += numLines($str);
283             # $default が partlist に足されてなかったら、先頭に足す... 逆か。
284             # args が、 $default を先頭から削る?
285             # fixup parts.
286 241         556 my Part $prev;
287 241         440 foreach my Part $part (@{$tmpl->{partlist}}) {
  241         661  
288 392 100       973 if ($prev) {
289 151 50       416 unless (defined $part->{cf_startpos}) {
290 0         0 die $self->synerror_at($self->{startln}, q{startpos is undef});
291             }
292 151 50       375 unless (defined $prev->{cf_bodypos}) {
293 0         0 die $self->synerror_at($self->{startln}, q{prev bodypos is undef});
294             }
295 151         365 $prev->{cf_bodylen} = $part->{cf_startpos} - $prev->{cf_bodypos};
296             }
297 392 100 100     1117 if ($part->{toks} and @{$part->{toks}}) {
  385         1312  
298             # widget 末尾の連続改行を、単一の改行トークンへ変換。(行番号は解析済みだから大丈夫)
299 380 100       2991 if ($part->{toks}[-1] =~ s/(?:\r?\n)+\Z//) {
300 335         784 push @{$part->{toks}}, "\n"
301 338 100       1018 unless $tmpl->{cf_ignore_trailing_newlines};
302             }
303             }
304 392 100       1726 if (my $sub = $part->can('fixup')) {
305 389         1253 $sub->($part, $tmpl, $self);
306             }
307 392         879 } continue { $prev = $part }
308 241 50       770 if ($prev) {
309 241         747 $prev->{cf_bodylen} = length($tmpl->{cf_string}) - $prev->{cf_bodypos};
310             }
311              
312 241         798 $self->finalize_template($tmpl);
313             }
314              
315             sub finalize_template {
316 241     241 0 588 (my MY $self, my Template $tmpl) = @_;
317 241 50       690 if ($self->{cf_match_argsroute_first}) {
318 0 0       0 if ($self->{rootroute}) {
319 0         0 $self->subroutes->append($self->{rootroute});
320             }
321             }
322 241 100       713 if ($self->{subroutes}) {
323 19         42 $tmpl->{cf_subroutes} = $self->{subroutes};
324             }
325             $tmpl
326 241         625 }
327              
328             sub parse_attlist {
329 520     520 0 995 my MY $self = shift;
330 520         1428 my ($for_decl) = my @opt = splice @_, 1;
331 520         899 my (@result);
332 520         1012 my $curln = $self->{endln};
333 520         6720 while ($_[0] =~ s{^$$self{re_att}}{}xs) {
334 1276         2570 my $start = $self->{curpos};
335 1276         2508 $self->{curpos} += length $&;
336             # startln は不変に保つ. これは add_part が startln を使うため
337 1276         3579 $self->{endln} += numLines($&);
338 1276 100 100     8009 next if $+{ws} || $+{comment};
339 633 100       2660 last if $+{nestclo};
340 620 50       2290 next if $+{macro}; #XXX: 今はまだ argmacro を無視!
341 620         1268 push @result, do {
342 620         1588 my @common = ($start, $self->{curpos}, $curln);
343 620 100 100     5930 if (not $+{attname} and $+{bare} and is_ident($+{bare})) {
    100 100        
    100 66        
344 349         1194 [TYPE_ATT_NAMEONLY, @common, split_ns($+{bare})];
345             } elsif ($+{nest}) {
346             [TYPE_ATT_NESTED, @common, $+{attname}
347 13         79 , $self->parse_attlist($_[0], @opt)];
348             } elsif ($+{entity} or $+{special}) {
349             # XXX: 間に space が入ってたら?
350 1 50       6 if ($+{lcmsg}) {
351             die $self->synerror_at($self->{startln}
352 0         0 , q{l10n msg is not allowed here});
353             }
354 1         7 [TYPE_ATT_TEXT, @common, $+{attname}, [$self->mkentity(@common)]];
355             } else {
356             # XXX: stringify したくなるかもだから、 sq/dq の区別も保存するべき?
357 257         935 my ($quote, $value) = oneof(\%+, qw(bare sq dq));
358             [!$quote && is_ident($value) ? TYPE_ATT_BARENAME : TYPE_ATT_TEXT
359             , @common, split_ns($+{attname})
360 257 100 100     1282 , $for_decl ? $value : $self->_parse_text_entities($value)];
    100          
361             }
362             };
363             } continue {
364 1263         2541 $curln = $self->{endln};
365 1263 100       6887 $self->_verify_token($self->{curpos}, $_[0]) if $self->{cf_debug};
366             }
367 520 50       2446 wantarray ? @result : \@result;
368             }
369              
370             sub mkentity {
371 379     379 0 822 (my MY $self) = shift;
372             # assert @_ == 3;
373 379         706 [TYPE_ENTITY, @_, do {
374 379 100       1677 if (my $ns = $+{entity}) {
    50          
375 377         1625 ($ns, $self->_parse_entpath);
376             } elsif (my $special = $+{special}) {
377 2         12 (undef, [call => $special
378             , $self->_parse_entpath(_parse_entgroup => ')')]);
379             } else {
380 0         0 die "mkentity called without entity or special";
381             }
382             }];
383             }
384              
385             sub split_ns {
386 606 100   606 0 2515 defined (my $value = shift)
387             or return undef; # make sure one scalar.
388 554         1146 local %+;
389 554         1484 my @names = split /:/, $value;
390 554 100       3058 @names > 1 ? \@names : $value;
391             }
392              
393             # widget の body の構文については、 Template が規定してよい。
394             sub parse_widget {
395 378     378 0 948 (my MY $self, my Widget $widget) = @_;
396 378         991 $self->{startln} = $self->{endln} = $widget->{cf_bodyln};
397             # XXX: 戻り値でも良い気はする。とはいえ、デバッグは楽か。
398 378   100     657 local $self->{chunklist} = my $chunks = [@{$widget->{toks} //= []}];
  378         1727  
399 378 100 66     2065 local $_ = @$chunks && !ref $chunks->[0] ? shift @$chunks : '';
400 378         980 $self->{startpos} = $self->{curpos} = $widget->{cf_bodypos};
401 378         1967 $self->_parse_body($widget, $widget->{tree} = []);
402 371         654 push @{$widget->{tree}}, nonmatched($_); # XXX: nest 時以外
  371         1185  
403 371         1439 $widget;
404             }
405              
406             sub _get_chunk {
407 708     708   1689 (my MY $self, my $sink) = @_;
408 708         1302 my $chunks = $self->{chunklist};
409 708 100       1843 if (length $_) {
410 455 50       1407 push @$sink, $_ if $sink;
411 455         1450 $self->{startln} = $self->{endln} += numLines($_);
412 455         1021 $self->{curpos} = $self->{startpos} += length $_;
413 455         854 $_ = '';
414             }
415             # comment の読み飛ばし
416 708   100     2795 while (@$chunks and ref $chunks->[0]) {
417 10         18 my $next = shift @$chunks;
418 10 50       28 push @$sink, $next if $sink;
419 10         20 $self->{startln} = $self->{endln} += $next->[NODE_BODY];
420 10         42 $self->{curpos} = $self->{startpos} = $next->[NODE_END];
421             }
422 708 100       2893 return unless @$chunks;
423 336         776 $_ = shift @$chunks;
424 336         1455 1
425             }
426              
427             sub nonspace {
428 799     799 0 2589 local (%+, $&, $1, $2);
429 799         6713 $_[0] =~ /\S/;
430             }
431              
432             sub splitline {
433 450     450 0 1607 local (%+, $&, $1, $2);
434 450         2792 split /(?<=\n)/, $_[0];
435             }
436              
437             sub _verify_token {
438 2033     2033   4377 (my MY $self, my $pos) = splice @_, 0, 2;
439 2033 50       4327 unless (defined $pos) {
440 0         0 die $self->synerror_at($self->{startln}, q{Token pos is undef!: now='%s'}, $_[0]);
441             }
442 2033         5726 my $tok = $self->{template}->source_substr($pos, length $_[0]);
443 2033 50       4766 unless (defined $tok) {
444 0         0 die $self->synerror_at($self->{startln}, q{Token substr is empty!: now='%s'}, $_[0]);
445             }
446 2033 50       13390 unless ($tok eq $_[0]) {
447 0         0 die $self->synerror_at($self->{startln}, q{Token mismatch!: substr='%s', now='%s'}
448             , $tok, $_[0]);
449             }
450             }
451              
452             sub drop_leading_ws {
453 10     10 0 23 my $list = shift;
454 10         45 local (%+, $1, $2, $&);
455 10   66     135 pop @$list while @$list and $list->[-1] =~ /^\s*$/s;
456             }
457              
458             #========================================
459             # build($ns, $kind, $partName, @attlist)
460             sub build {
461 395     395 0 1206 (my MY $self, my ($ns, $kind, $partName)) = splice @_, 0, 4;
462             $self->can("build_$kind")->
463             ($self, name => $partName, kind => $kind
464             , folder => $self->{template}
465 395         2413 , startpos => $self->{startpos}, @_);
466             }
467             # 今度はこっちが今一ね。
468 126     126 0 1099 sub build_widget { shift->Widget->new(@_) }
469 266     266 0 2178 sub build_page { shift->Page->new(@_) }
470             sub build_action {
471 3     3 0 15 (my MY $self, my (%opts)) = @_;
472              
473             # XXX: This is a workaround to have renderers and actions
474             # in the same hash table $template->{Item}.
475 3         8 $opts{name} = "do_$opts{name}";
476              
477 3         26 $self->Action->new(%opts);
478             }
479 0     0 0 0 sub build_data { shift->Data->new(@_) }
480              
481             #========================================
482             # declare
483             sub declare_base {
484 7     7 0 24 (my MY $self, my Template $tmpl, my ($ns, @args)) = @_;
485              
486 7         34 $self->{cf_vfs}->declare_base($self, $tmpl, $ns, @args);
487              
488 7         65 undef;
489             }
490              
491             sub declare_args {
492 93     93 0 335 (my MY $self, my Template $tmpl, my $ns) = splice @_, 0, 3;
493 93         194 my Part $newpart = do {
494             # 宣言抜きで作られていた part を一旦一覧から外す。
495 93         268 my Part $oldpart = delete $tmpl->{Item}{''};
496 93 50       318 unless ($oldpart->{cf_implicit}) {
497 0         0 die $self->synerror_at($self->{startln}, q{Duplicate !%s:args declaration}, $ns);
498             }
499 93 100       195 if (@{$tmpl->{partlist}} == 1) {
  93         338  
500             # 先頭だったら再利用。
501 91         166 shift @{$tmpl->{partlist}}; # == $oldpart
  91         294  
502             } else {
503 2         5 $oldpart->{cf_suppressed} = 1; # 途中なら、古いものを隠して、新たに作り直し。
504             $self->build($ns, $self->default_part_for($tmpl), ''
505 2         9 , startln => $self->{startln});
506             }
507             };
508 93         232 $newpart->{cf_startpos} = $self->{startpos};
509 93         263 $newpart->{cf_bodypos} = $self->{curpos} + 1;
510 93         347 $self->add_part($tmpl, $newpart); # partlist と Item に足し直す
511              
512 93 100 66     817 if (@_ and $_[0] and $_[0]->[NODE_TYPE] == TYPE_ATT_TEXT
      66        
      100        
513             and not defined $_[0]->[NODE_PATH]) {
514 3         5 my $patNode = shift;
515             my $mapping = $self->parse_location($patNode->[NODE_BODY], '', $newpart)
516 3 50       11 or do {
517             die $self->synerror_at($self->{startln}
518 0         0 , q{Invalid location in %s:%s - "%s"}
519             , $ns, 'args', $patNode->[NODE_BODY])
520             };
521 3 50       11 if ($self->{cf_match_argsroute_first}) {
522 0         0 $self->{rootroute} = $mapping;
523             } else {
524 3         19 $self->{subroutes}->append($mapping);
525             }
526 3         12 $self->add_url_params($newpart, lexpand($mapping->cget('params')));
527             }
528              
529 93         487 $self->add_args($newpart, @_);
530 92         312 $newpart;
531             }
532              
533             #
534             sub declare_config {
535 4     4 0 17 (my MY $self, my Template $tmpl, my ($ns, @args)) = @_;
536             # XXX: 一方が undef だったら?
537 4   100     12 $tmpl->configure(map {($_->[NODE_PATH], $_->[NODE_BODY] // 1)} @args);
  4         34  
538 4         26 undef;
539             }
540              
541             sub declare_constants {
542 0     0 0 0 (my MY $self, my Template $tmpl, my ($ns, @args)) = @_;
543 0         0 $tmpl->{cf_constants} = \@args;
544 0         0 undef;
545             }
546              
547             #========================================
548              
549             sub location2name {
550 33     33 0 86 (my MY $self, my $location) = @_;
551 33         175 $location =~ s{([^A-Za-z0-9])}{'_'.sprintf("%02x", unpack("C", $1))}eg;
  35         299  
552 33         146 $location;
553             }
554              
555             sub parse_location {
556 38     38 0 108 (my MY $self, my ($location, $name, $item)) = @_;
557 38 50       170 return unless $location =~ m{^/};
558 38         123 $self->subroutes->create([$name, $location], $item);
559             }
560              
561             sub subroutes {
562 38     38 0 87 (my MY $self) = @_;
563 38   66     195 $self->{subroutes} //= $self->SubRoutes->new;
564             }
565              
566             sub SubRoutes {
567 19     19 0 1206 require YATT::Lite::WebMVC0::SubRoutes;
568 19         176 'YATT::Lite::WebMVC0::SubRoutes'
569             }
570              
571             #========================================
572             sub primary_ns {
573 244     244 0 512 my MY $self = shift;
574 244 50       720 unless ($self->{cf_namespace}) {
575 0         0 'yatt';
576             } else {
577 244         738 first($self->{cf_namespace});
578             }
579             }
580             sub namespace {
581 457     457 0 797 my MY $self = shift;
582 457 50       1207 return unless defined $self->{cf_namespace};
583             ref $self->{cf_namespace} && wantarray
584 457         4128 ? @{$self->{cf_namespace}}
585 457 50 33     2198 : $self->{cf_namespace};
586             }
587              
588             #========================================
589             sub add_part {
590 486     486 0 1065 (my MY $self, my Template $tmpl, my Part $part) = @_;
591 486 50       1753 if (defined $tmpl->{Item}{$part->{cf_name}}) {
592 0         0 die $self->synerror_at($self->{startln}, q{Conflicting part name! '%s'}, $part->{cf_name});
593             }
594 486 100 100     2134 if ($tmpl->{partlist} and my Part $prev = $tmpl->{partlist}[-1]) {
595 151         385 $prev->{cf_endln} = $self->{endln};
596             }
597 486         963 $part->{cf_startln} = $self->{startln};
598 486         877 $part->{cf_bodyln} = $self->{endln};
599 486         901 push @{$tmpl->{partlist}}, $tmpl->{Item}{$part->{cf_name}} = $part;
  486         1836  
600             }
601              
602             sub add_text {
603 156     156 0 534 (my MY $self, my Part $part, my $text) = @_;
604 156         295 push @{$part->{toks}}, $text;
  156         456  
605 156         614 $self->add_posinfo(length($text), 1);
606 156         549 $self->{startln} = $self->{endln} += numLines($text);
607             }
608              
609             sub add_lineinfo {
610 639     639 0 2035 (my MY $self, my $sink) = @_;
611             # push @$sink, [TYPE_LINEINFO, $self->{endln}];
612             }
613              
614             sub parse_arg_spec_for_part {
615 1     1 0 4 (my MY $self, my Part $part, my $attNode) = @_;
616             my ($node_type, $lno, $argName, $desc, @rest)
617 1         3 = @{$attNode}[NODE_TYPE, NODE_LNO, NODE_PATH, NODE_BODY
  1         4  
618             , NODE_BODY+1 .. $#$attNode];
619 1         4 my ($type, $dflag, $default);
620 1 50       5 if ($node_type == TYPE_ATT_NESTED) {
621 0   0     0 $type = $desc->[NODE_PATH] || $desc->[NODE_BODY];
622             # primary of [primary key=val key=val] # delegate:foo の時は BODY に入る?
623             } else {
624 1   50     9 ($type, $dflag, $default) = split m{([|/?!])}, $desc || '', 2;
625             };
626 1 50       5 ($type, $argName, nextArgNo($part)
627             , $lno, $node_type, $dflag
628             , defined $default
629             ? $self->_parse_text_entities($default) : undef);
630             }
631              
632             sub add_args {
633 251     251 0 724 (my MY $self, my Part $part) = splice @_, 0, 2;
634 251         644 foreach my $argSpec (@_) {
635             # XXX: Rewrite this with parse_arg_spec_for_part!
636              
637             # XXX: text もあるし、 %yatt:argmacro; もある。
638             my ($node_type, $lno, $argName, $desc, @rest)
639 281         695 = @{$argSpec}[NODE_TYPE, NODE_LNO, NODE_PATH, NODE_BODY
  281         868  
640             , NODE_BODY+1 .. $#$argSpec];
641 281 50       804 unless (defined $argName) {
642 0         0 die $self->synerror_at($self->{startln}, 'Invalid argument spec');
643             }
644              
645 281         547 my ($type, $dflag, $default);
646 281 100       655 if ($node_type == TYPE_ATT_NESTED) {
647 12   33     51 $type = $desc->[NODE_PATH] || $desc->[NODE_BODY];
648             # primary of [primary key=val key=val] # delegate:foo の時は BODY に入る?
649             } else {
650 269   100     1269 ($type, $dflag, $default) = split m{([|/?!])}, $desc || '', 2;
651             };
652              
653 281 100       889 if (my $var = $part->{arg_dict}{$argName}) {
654 1 50       3 if ($var->from_route) {
655             # Override $type, $dflag, $default of this var.
656 1         7 $self->set_var_type($var, $type); # type is always overridden.
657 1 50       6 $var->dflag($dflag) if $dflag;
658 1 50       10 $var->default($self->_parse_text_entities($default))
659             if defined $default;
660             } else {
661             die $self->synerror_at($self->{startln}
662             , 'Argument %s redefined in %s %s'
663 0         0 , $argName, $part->{cf_kind}, $part->{cf_name});
664             }
665             } else {
666             my $var = $self->mkvar_at($self->{startln}
667 280 100       1197 , $type, $argName, nextArgNo($part)
668             , $lno, $node_type, $dflag
669             , defined $default
670             ? $self->_parse_text_entities($default) : undef);
671              
672 279 100       658 if ($node_type == TYPE_ATT_NESTED) {
673             # XXX: [delegate:type ...], [code ...] の ... が来る
674             # 仮想的な widget にする? のが一番楽そうではあるか。そうすれば add_args 出来る。
675             # $self->add_arg_of_delegate/code/...へ。
676 12         52 my $t = $var->type->[0];
677             my $sub = $self->can("add_arg_of_type_$t")
678 12 50       78 or die $self->synerror_at($self->{startln}, "Unknown arg type in arg '%s': %s", $argName, $t);
679 12         46 $sub->($self, $part, $var, \@rest);
680             } else {
681 267         717 push @{$part->{arg_order}}, $argName;
  267         731  
682 267         963 $part->{arg_dict}{$argName} = $var;
683             }
684             }
685             }
686 250         437 $self;
687             }
688              
689             sub add_url_params {
690 38     38 0 98 (my MY $self, my Part $part, my @params) = @_;
691 38         95 foreach my $param (@params) {
692 6         14 my ($argName, $type_or_pat) = @$param;
693 6         12 my $type = 'value'; # XXX: type_or_pat
694 6         22 my $var = $self->mkvar_at($self->{startln}, $type, $argName
695             , nextArgNo($part));
696 6         25 $var->from_route(1);
697 6         9 push @{$part->{arg_order}}, $argName;
  6         17  
698 6         34 $part->{arg_dict}{$argName} = $var;
699             }
700             }
701              
702             # code 型は仮想的な Widget を作る。
703             sub add_arg_of_type_code {
704 9     9 0 32 (my MY $self, my Part $part, my ($var, $attlist)) = @_;
705 9         50 $var->widget(my Widget $virtual = $self->Widget->new(name => $var->varname));
706 9         48 $self->add_args($virtual, @$attlist);
707 9         31 my $argName = $var->varname;
708 9         29 push @{$part->{arg_order}}, $argName;
  9         26  
709 9         36 $part->{arg_dict}{$argName} = $var;
710             }
711              
712             sub add_arg_of_type_delegate {
713 3     3 0 9 (my MY $self, my Widget $widget, my ($var, $attlist)) = @_;
714             # XXX: 引数でない変数も足さないと...
715 3         14 my $name = $var->varname;
716             # XXX: 既に有ったらエラーにしないと。
717 3         10 $widget->{var_dict}{$name} = $var;
718 3         7 my ($type, @subtype) = @{$var->type};
  3         11  
719 3 100       11 my @wpath = @subtype ? @subtype : $name;
720             my Widget $delegate = $self->{cf_vfs}->find_part_from
721 3 50       15 ($widget->{cf_folder}, @wpath) or do {
722 0         0 $self->synerror_at($self->{startln}, "Can't find delegate widget for argument %s=[%s]", $name, join(":", $type, @subtype));
723             };
724 3         14 $var->weakened_set_widget($delegate);
725 3 50       13 unless (Scalar::Util::isweak($var->[YATT::Lite::VarTypes::t_delegate::VSLOT_WIDGET])) {
726 0         0 die "Can't weaken!";
727             }
728 3         12 $var->delegate_vars(\ my %delegate_vars);
729              
730 3         7 my ($attDict, $excludeDict) = do {
731 3         7 my (%attDict, %exclDict);
732 3         8 foreach my $argSpec (@$attlist) {
733 2 100 33     17 if (my $attName = $argSpec->[NODE_PATH]) {
    50          
734 1 50       5 defined $attDict{$attName}
735             and die $self->synerror_at
736             ($argSpec->[NODE_LNO]
737             , "Duplicate argname '%s' in delegate var %s"
738             , $attName, $name);
739 1         4 $attDict{$attName} = $argSpec;
740             } elsif ($argSpec->[NODE_TYPE] == TYPE_ATT_TEXT
741             and ($attName) = $argSpec->[NODE_BODY] =~ /^-(\w+)$/) {
742 1 50       6 if (not $delegate->{arg_dict}{$attName}) {
743 0         0 die $self->synerror_at
744             ($argSpec->[NODE_LNO]
745             , "No such argument '%s' in delegate to '%s'"
746             , $attName, $name);
747             }
748 1         4 $exclDict{$attName} = $argSpec;
749             } else {
750 0         0 die $self->synerror_at
751             ($argSpec->[NODE_LNO]
752             , "Invalid decl spec for delegate var %s", $name);
753             }
754             }
755 3         9 (\%attDict, \%exclDict);
756             };
757              
758 3         6 foreach my $argName (@{$delegate->{arg_order}}) {
  3         11  
759             # 既に宣言されている名前は、足さない。
760 8 50       23 next if $widget->{arg_dict}{$argName};
761              
762             # Ignore [delegate -excluded_var]
763 8 100       31 next if $excludeDict->{$argName};
764              
765 7         18 $delegate_vars{$argName} = my $orig = $delegate->{arg_dict}{$argName};
766              
767 7         15 my $actual = do {
768 7 100       16 if (my $att = $attDict->{$argName}) {
769 1         5 my @new = $self->parse_arg_spec_for_part($widget, $att);
770 1   33     10 $new[0] ||= $orig->[0];
771 1         5 $self->mkvar_at($self->{startln}, @new);
772             } else {
773             # clone して argno と lineno を変える。
774             $self->mkvar_at($widget->{cf_startln}, @$orig)
775 6         18 ->argno(nextArgNo($widget))->lineno($widget->{cf_startln});
776             }
777             };
778 7         16 $widget->{arg_dict}{$argName} = $actual;
779             # XXX: lineno を widget の startln にするのは手抜き。本来は直前の arg のものを使うべき。
780 7         12 push @{$widget->{arg_order}}, $argName;
  7         24  
781             }
782             }
783             sub nextArgNo {
784 293     293 0 613 (my Part $part) = @_;
785 293 100       1287 $part->{arg_order} ? scalar @{$part->{arg_order}} : 0;
  118         490  
786             }
787              
788             #========================================
789             sub synerror_at {
790 19     19 0 55 (my MY $self, my $ln) = splice @_, 0, 2;
791 19         65 my %opts = ($self->_tmpl_file_line($ln), depth => 2);
792 19         64 $self->_error(\%opts, @_);
793             }
794              
795             sub _error {
796 20     20   60 (my MY $self, my ($opts, $fmt)) = splice @_, 0, 3;
797 20 100       57 if (my $vfs = $self->{cf_vfs}) {
798 10         55 $vfs->error($opts, $fmt, @_);
799             } else {
800 10         98 sprintf($fmt, @_);
801             }
802             }
803              
804             sub _tmpl_file_line {
805 20     20   38 (my MY $self, my $ln) = @_;
806 20 100       122 ($$self{cf_path} ? (tmpl_file => $$self{cf_path}) : ()
    100          
807             , defined $ln ? (tmpl_line => $ln) : ());
808             }
809              
810             #========================================
811             sub is_ident {
812 460 50   460 0 1567 return undef unless defined $_[0];
813 460         1047 local %+;
814 460         3302 $_[0] =~ m{^[[:alpha:]_\:](?:\w+|:)*$}; # To exclude leading digit.
815             }
816              
817             sub oneof {
818 257     257 0 525 my $hash = shift;
819 257         482 my $i = 0;
820 257         612 foreach my $key (@_) {
821 543 100       2265 if (defined(my $value = $hash->{$key})) {
822 257         905 return $i => $value;
823             }
824             } continue {
825 286         600 $i++;
826             }
827 0         0 die "really??";
828             }
829              
830 244 50   244 0 1210 sub first { ref $_[0] ? $_[0][0] : $_[0] }
831              
832             sub nonmatched {
833 612 100 66 612 0 3078 return unless defined $_[0] and length $_[0];
834 235         627 $_[0];
835             }
836              
837             sub shortened_original_entpath {
838 8     8 0 17 (my MY $self) = @_;
839 8         17 my $str = $self->{_original_entpath};
840 8         24 $str =~ s/\n.*\z//s;
841 8         28 $str;
842             }
843              
844             #========================================
845              
846             sub _parse_body;
847              
848             sub _parse_text_entities;
849             sub _parse_entpath;
850             sub _parse_pipeline;
851             sub _parse_entgroup;
852             sub _parse_entterm;
853             sub _parse_group_string;
854             sub _parse_hash;
855              
856       0     sub DESTROY {}
857              
858             sub AUTOLOAD {
859 10 50   10   34 unless (ref $_[0]) {
860 0         0 confess "BUG! \$self isn't object!";
861             }
862 10         20 my $sub = our $AUTOLOAD;
863 10         77 (my $meth = $sub) =~ s/.*:://;
864 10 50       55 my $sym = $YATT::Lite::LRXML::{$meth}
865             or croak "No such method: $meth";
866 10         25 given ($meth) {
867 10         30 when (/ent/) { require YATT::Lite::LRXML::ParseEntpath }
  5         2525  
868 5         26 when (/body/) { require YATT::Lite::LRXML::ParseBody }
  5         3111  
869 0         0 default {
870 0         0 my MY $self = $_[0];
871 0         0 die $self->synerror_at($self->{startln}, "Unknown method: %s", $meth);
872             }
873             }
874 10         66 my $code = *{$sym}{CODE}
875 10 50       24 or croak "Can't find definition of: $meth";
876 10         52 goto &$code;
877             }
878              
879             #
880 17     17   207 use YATT::Lite::Breakpoint qw(break_load_parser break_parser);
  17         53  
  17         1436  
881             break_load_parser();
882              
883             1;