File Coverage

blib/lib/YATT/Lite/LRXML.pm
Criterion Covered Total %
statement 400 442 90.5
branch 140 192 72.9
condition 59 95 62.1
subroutine 65 70 92.8
pod 1 49 2.0
total 665 848 78.4


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