File Coverage

blib/lib/YATT/Lite/CGen/Perl.pm
Criterion Covered Total %
statement 529 594 89.0
branch 226 288 78.4
condition 83 117 70.9
subroutine 82 91 90.1
pod 0 73 0.0
total 920 1163 79.1


line stmt bran cond sub pod time code
1             package YATT::Lite::CGen::Perl;
2 15     15   6543 use strict;
  15         41  
  15         622  
3 15     15   78 use warnings qw(FATAL all NONFATAL misc);
  15         128  
  15         528  
4 15     15   87 use mro 'c3';
  15         34  
  15         172  
5              
6             require 5.010; # For named capture.
7              
8 15     15   655 use constant DEBUG_MRO => $ENV{DEBUG_YATT_MRO};
  15         30  
  15         1135  
9              
10 15     15   94 use YATT::Lite::Core qw(Folder Template Part Widget Action);
  15         44  
  15         850  
11 15     15   106 use YATT::Lite::Constants;
  15         35  
  15         2647  
12              
13             # Naming convention:
14             # generate_SRC -- Public Interface.
15             # gen_DETAIL -- Internal higher/large tasks.
16             # from_NODETYPE -- Node Type specific dispatch entry.
17             # as_WHATHOW_FROM -- Miscellaneous dispatching (for var type and others)
18              
19             {
20             #========================================
21             package YATT::Lite::CGen::Perl; sub MY () {__PACKAGE__}
22 15     15   101 use base qw(YATT::Lite::CGen);
  15         32  
  15         6078  
23 15     15   114 use YATT::Lite::MFields;
  15         34  
  15         85  
24 15     15   101 use YATT::Lite::Util qw(lexpand numLines globref terse_dump catch);
  15         36  
  15         807  
25 15     15   101 use Carp;
  15         29  
  15         104995  
26             #========================================
27             sub list_inheritance {
28 440     440 0 924 (my MY $self, my Template $tmpl) = @_;
29             # XXX: Duplicate detection should be handled higer layer.
30 440         715 my %dup;
31             map {
32 440         1302 my Folder $f = $_;
  452         808  
33 452 50       1280 unless (defined $f->{cf_entns}) {
34 0         0 die "BUG: EntNS is empty for ".terse_dump($f->{cf_name})."!";
35             }
36 452 50       1471 if ($dup{$f->{cf_entns}}++) {
37             ()
38 0         0 } else {
39             $f->{cf_entns}
40 452         1703 }
41             } $tmpl->list_base
42             }
43             sub setup_inheritance_for {
44 220     220 0 620 (my MY $self, my $spec, my Template $tmpl) = @_;
45 220 50       752 unless (defined $tmpl->{cf_entns}) {
46 0         0 die "BUG: EntNS is empty for '$tmpl->{cf_name}'!";
47             }
48 220         829 my $glob = globref($$tmpl{cf_entns}, 'ISA');
49             # XXX: base change should be reflected when reloaded, but...
50 220 50       770 unless (defined $glob) {
51 0         0 die "BUG: ISA glob for '$tmpl->{cf_name}' is empty!";
52             }
53 220         1102 $self->ensure_generated_for_folders($spec, $tmpl->list_base);
54 220         922 my @isa = $self->list_inheritance($tmpl);
55 220 50       537 if (grep {not defined} @isa) {
  226         798  
56 0         0 die "BUG: ISA for '$tmpl->{cf_name}' contains undef!";
57             }
58 220 50       1598 if (my $err = catch {
59 220     220   3874 *$glob = \@isa;
60             }) {
61             die $self->generror("Can't set ISA for '%s' as [%s]: %s"
62             , $tmpl->{cf_name}
63 0         0 , join(", ", @isa)
64             , $err
65             );
66             }
67             }
68             sub generate_inheritance {
69 220     220 0 593 (my MY $self, my Template $tmpl) = @_;
70 220         597 my @isa = $self->list_inheritance($tmpl);
71 220         793 my $mro = mro::get_mro($tmpl->{cf_entns});
72 220         387 print STDERR "($mro) [$tmpl->{cf_path}] $tmpl->{cf_entns}::ISA = @isa\n"
73             if DEBUG_MRO;
74 220         1046 sprintf q{use mro '%s'; our @ISA = qw(%s); }, $mro, join " ", @isa;
75             }
76             #========================================
77             sub generate_preamble {
78 220     220 0 539 (my MY $self, my Template $tmpl) = @_;
79 220   33     645 $tmpl ||= $self->{curtmpl};
80 220         390 my @stats;
81 220 50       758 unless ($self->{cf_no_lineinfo}) {
82 220         723 my $line = qq{#line }. $self->{curline};
83 220 100       899 if (defined(my $fn = $tmpl->fake_filename)) {
84             # cf_name is dummy filename.
85 219         678 $line .= qq{ "$fn"};
86             }
87 220         858 push @stats, $line .= "\n";
88             }
89             push @stats, sprintf q{package %s; use strict; use warnings; use 5.010; }
90 220         1137 , $$tmpl{cf_entns};
91 220         966 push @stats, $self->generate_inheritance($tmpl);
92 220 100       741 push @stats, "use utf8; " if $$tmpl{cf_utf8};
93 220 100       707 push @stats, q|no warnings qw(redefine); | if $$tmpl{cf_age}++;
94 220         505 push @stats, sprintf q|sub filename {__FILE__}; |;
95             @stats
96 220         743 }
97             sub generate_page {
98             # XXX: 本物へ。 public フラグ?
99 253     253 0 1037 shift->generate_widget(@_);
100             }
101             sub generate_widget {
102 346     346 0 1122 (my MY $self, my Widget $widget, my ($widget_name, $tmpl_path)) = @_;
103 346 100       1026 if ($widget->{cf_suppressed}) {
104 2         12 return "\n" x ($widget->{cf_endln} - $widget->{cf_startln});
105             }
106 344         1430 break_cgen();
107 344         818 local $self->{curwidget} = $widget;
108             # XXX: calling convention 周り, body の code 型
109             local $self->{scope} = $self->mkscope
110             ({}, $widget->{var_dict}, $widget->{arg_dict} ||= {}
111 344   50     2376 , {this => $self->mkvar_at(undef, text => 'this')
112             , 'CON' => $self->mkvar_at(undef, text => 'CON')
113             , '_' => $self->mkvar_at(undef, text => '_')}
114             );
115 344         812 local $self->{curtoks} = [@{$widget->{tree}}];
  344         1147  
116             ($self->sync_curline($widget->{cf_startln})
117             , "sub render_$$widget{cf_name} {"
118             , $self->gen_preamble($widget)
119             , $self->gen_getargs($widget, not $widget->{cf_implicit})
120 344         1438 , $self->as_print("}")
121             );
122             }
123             sub generate_action {
124 3     3 0 8 (my MY $self, my Action $action) = @_;
125             # XXX: 改行の調整が必要。
126             my @src = ($self->sync_curline($action->{cf_startln})
127 3         11 , "sub $$action{cf_name} {");
128             my $src = $self->{curtmpl}->source_substr
129 3         15 ($action->{cf_bodypos}, $action->{cf_bodylen});
130              
131 3 100 66     11 if (lexpand($action->{arg_order})
132             or $src !~ m{^([\ \t\r\n]*)my\s*\([^;\)]+\)\s*=\s*\@_\s*;}) {
133             # If an action has no arguments
134             # and its source doesn't start with my (...) = @_;,
135             # insert preamble and getargs.
136             push @src, $self->gen_preamble($action)
137 1         5 , $self->gen_getargs($action, not $action->{cf_implicit});
138             }
139              
140 3         13 my $has_nl = $src =~ s/\r?\n\Z//;
141 3 50       10 $self->{curline} = $action->{cf_bodyln} + numLines($src)
142             + ($has_nl ? 1 : 0);
143 3         19 (@src, $src, "}");
144             }
145             #========================================
146 345     345 0 1538 sub gen_preamble {q{ my ($this, $CON) = splice @_, 0, 2;}}
147             sub gen_getargs {
148 377     377 0 903 (my MY $self, my Widget $widget, my $for_decl) = @_;
149 377         634 my @res;
150 377         1301 foreach my $argName (lexpand($widget->{arg_order})) {
151             # デフォルト値と、型と。
152 588         1286 my $var = $widget->{arg_dict}{$argName};
153 588 100       2530 push @res, $for_decl ? $self->sync_curline($var->lineno) : ()
154             , sprintf q{ my %s = %s;}, $self->as_lvalue($var)
155             , $self->as_getarg($var);
156             # shift しない方が、debug 時に stack trace に引数値が見えて嬉しい。
157             }
158             # 末尾の改行
159 377 100 33     1287 push @res, "\n" and $self->{curline}++ if $for_decl;
160             (@res, $self->sync_curline($widget->{cf_bodyln})
161 377         1511 , $self->cut_next_nl);
162             }
163             sub as_getarg {
164 588     588 0 1294 (my MY $self, my $var) = @_;
165 588         1701 my $actual = '$_['.$var->argno.']';
166 588 100 66     2003 return $actual unless defined (my $default = $var->default)
167             and defined (my $mode = $var->dflag);
168 28         80 my $varname = $self->as_lvalue($var);
169 28 100       102 if ($mode eq "!") {
170 4         15 return qq{defined $actual ? $actual : }
171 4         12 . qq{die q|Argument '@{[$var->varname]}' is undef!|};
172             }
173             # XXX: do given/when は値を返さないから、ここでは使えない! void context 扱いになっちまう。
174 24         58 my ($cond) = do {
175 24 100       96 if ($mode eq "|") {
    100          
    50          
176 2         8 qq{$actual}
177             } elsif ($mode eq "?") {
178 20         74 qq{defined $actual && $actual ne ""}
179             } elsif ($mode eq "/") {
180 2         9 qq{defined $actual}
181             } else {
182 0         0 die "Unknown defaulting mode: $mode"
183             }
184             };
185 24         116 sprintf q{(%s ? %s : %s)}, $cond, $actual
186             , $self->as_cast_to($var, $default);
187             # XXX: html 型変数へ text 型変数の混じったデフォルト値を入れるときには、 as_text じゃだめ
188             # as_text に、やはり escape flag を渡せるようにするのが筋か?
189             }
190             #========================================
191             our @DISPATCH;
192             $DISPATCH[TYPE_LINEINFO] = \&from_lineinfo;
193             $DISPATCH[TYPE_COMMENT] = \&from_comment;
194             $DISPATCH[TYPE_LCMSG] = \&from_lcmsg;
195             $DISPATCH[TYPE_ENTITY] = \&from_entity;
196             $DISPATCH[TYPE_PI] = \&from_pi;
197             $DISPATCH[TYPE_ELEMENT] = \&from_element;
198             $DISPATCH[TYPE_ATT_NESTED] = \&from_elematt;
199             sub as_print {
200 403     403 0 1066 (my MY $self, my ($last, $localtoks)) = @_;
201 403 50       1031 push @{$self->{curtoks}}, @$localtoks if $localtoks;
  0         0  
202 403         984 local $self->{needs_escaping} = 1;
203 403         1112 my (@result, @queue) = '';
204             # curline は queue 詰めの外側で操作する。
205             # $last は一回だけ出力するように、undef が必要。
206             my $flush = sub {
207 1200     1200   2509 my ($has_nl, $task, $pad) = @_;
208 1200 100       2847 push @result, $pad if defined $pad;
209 1200 100       3862 push @result, q{print $CON (}.join(", ", @queue).");" if @queue;
210             # もう token が残っていなくて、かつ $last が与えられていたら、 $last を足す。
211 1200 100       2950 push @result, $task->() if $task;
212 1200 100 50     2908 $result[-1] .= $last and undef $last if $last and not @{$self->{curtoks}};
  861   100     3543  
213             # 明示 "\n" が来ていた場合は、 ";" と同時に改行する。
214 1200 100       2998 $result[-1] .= "\n" if $has_nl;
215 1200         2746 undef @queue;
216 403         2213 };
217 403         843 while (@{$self->{curtoks}}) {
  1854         4749  
218 1472         2299 my $node = shift @{$self->{curtoks}};
  1472         3011  
219 1472 100       3523 unless (ref $node) {
220             # text node の末尾が改行で終わっている場合、 明示的に "\n" を生成する
221 901         3393 my $has_nl = $node =~ s/\r?\n\Z//s;
222 901 100       3343 push @queue, qtext($node) if $node ne ''; # 削ったら空になるかも。
223 901         2856 $self->{curline} += numLines($node);
224 901 100       2195 $self->{curline}++ if $has_nl;
225             push @queue, q{"\n"} if $has_nl
226 901 100 100     2253 and @{$self->{curtoks}} || not $self->{no_last_newline};
      100        
227 901 100 100     3695 $flush->($has_nl) if $has_nl || $node =~ /\n/;
228 901         2040 next;
229             }
230 571   100     1751 my $pad = $self->sync_curline($node->[NODE_LNO]) // '';
231 571 50       1804 my $sub = $DISPATCH[$node->[0]]
232             or die $self->generror("Unknown node type: %d", $node->[0]);
233 571         1535 my $expr = $sub->($self, $node);
234 550 50       1687 unless (defined $expr) {
235 0         0 push @result, $self->cut_next_nl;
236 0         0 next;
237             }
238 550 100       1311 if (ref $expr) {
239 233     233   1428 $flush->(undef, sub { ("$$expr;", $self->cut_next_nl) }, $pad);
  233         945  
240             } else {
241 317 100       869 $flush->(undef, undef, $pad) if length $pad;
242 317         645 push @queue, $expr;
243 317 50       1170 $flush->() if $expr =~ /\n/;
244             }
245             }
246 382         1053 $flush->();
247 382         6228 join " ", @result;
248             }
249             sub gen_as {
250 78     78 0 273 (my MY $self, my ($type, $dispatch, $escape, $text_quote))
251             = splice @_, 0, 5;
252 78         213 local $self->{needs_escaping} = $escape;
253 78         137 my (@result);
254             # Empty expr (ie <:yatt:arg>) should generate q|| as code.
255 78 100 66     265 if (not @_ and $text_quote) {
256 1         4 push @result, qtext('');
257             }
258 78         233 while (@_) {
259 135         262 my $node = shift;
260 135 100       347 unless (ref $node) {
261 103 100       305 push @result, ($text_quote ? qtext($node) : $node);
262 103         365 $self->{curline} += numLines($node);
263 103         308 next;
264             }
265             # 許されるのは entity だけでは? でもないか。 element 引数の時は、capture したいはず。
266 32 50       119 my $sub = $dispatch->[$node->[0]]
267             or die $self->generror("gen_as %s: Unknown node type: %d"
268             , $type, $node->[0]);
269 32         103 my $expr = $sub->($self, $node);
270 32 50       95 next unless defined $expr;
271 32 50       87 if (ref $expr) {
272 0         0 die $self->generror("Syntax error, not allowed here: %s", $$expr);
273             }
274 32         103 push @result, $expr;
275             }
276 78 100       492 wantarray ? @result : join("", @result);
277             }
278              
279             # as_list と対になる。
280             our @AS_TEXT;
281             $AS_TEXT[TYPE_LCMSG] = \&from_lcmsg;
282             $AS_TEXT[TYPE_ENTITY] = \&from_entity;
283             $AS_TEXT[TYPE_PI] = \&text_from_pi;
284             $AS_TEXT[TYPE_ELEMENT] = \&text_from_element; # XXX: ?? Used??
285             $AS_TEXT[TYPE_ATT_NESTED] = sub {undef}; # gen_as が scalar 受けゆえ
286             # as_text は、escape 不要。なぜなら、 print 時に escape されるから。
287             # でも、 escape 有無を flag で渡せた方が、 html 型にも使えて便利では?
288             # というか、 html 型には capture が必要か。 capture は buffering したいよね?
289             sub as_text {
290 14     14 0 61 join '.', shift->gen_as(text => \@AS_TEXT, 0, 1, @_);
291             }
292              
293             our @AS_LIST;
294             $AS_LIST[TYPE_ENTITY] = \&from_entity;
295             $AS_LIST[TYPE_PI] = \&list_from_pi;
296             $AS_LIST[TYPE_ELEMENT] = \&list_from_element;
297             $AS_LIST[TYPE_ATT_NESTED] = sub {undef}; # XXX: 微妙
298             sub as_list {
299 52     52 0 284 shift->gen_as(list => \@AS_LIST, 0, 0, @_);
300             }
301             #========================================
302             sub from_element {
303             # XXX: macro (if, foreach, my, format) (error if は?)
304 206     206 0 480 (my MY $self, my $node) = @_;
305 206         422 my $path = $node->[NODE_PATH];
306 206 50 66     960 if (my $alt = $self->altgen($path->[0])) {
    100 100        
307 0         0 qtext($alt->($node));
308             } elsif (@$path == 2
309             and my $macro = $self->can("macro_" . join "_", @$path)
310             || $self->can("macro_$path->[-1]")) {
311 36         133 $macro->($self, $node);
312             } else {
313             # stack trace に現れるように, 敢えて展開。
314 170         377 $self->gen_call($node, @{$node->[NODE_PATH]});
  170         718  
315             }
316             }
317             sub text_from_element {
318 2     2 0 7 (my MY $self, my $node) = @_;
319 2         15 &YATT::Lite::Breakpoint::breakpoint();
320 2         18 my $call_ref = $self->from_element($node);
321 2         13 sprintf q{YATT::Lite::Util::captured {my ($CON) = @_; %s}}, $$call_ref;
322             }
323              
324             sub gen_call {
325 170     170 0 609 (my MY $self, my ($node, @path)) = @_;
326 170         417 my $wname = join ":", @path;
327 170 100 100     996 if (@path == 2 and my $var = $self->find_callable_var($path[-1])) {
328             # code 引数の中の引数のデフォルト値の中に、改行が有ったら??
329             # XXX: body の引数宣言が無い場合に は、ちゃんと呼び出せるか?
330 23         96 return $self->can("as_varcall_" . $var->type->[0])
331             ->($self, $var, $node);
332             }
333              
334 147 100       942 my Widget $widget = $self->lookup_widget(@path) or do {
335 2         9 my $err = $self->generror(q{No such widget <%s>}, $wname);
336 2         98 die $err;
337             };
338              
339 145         693 $self->ensure_generated(perl => my Template $tmpl = $widget->{cf_folder});
340 145         444 my $use_this = $tmpl == $self->{curtmpl};
341 145 100       428 unless ($use_this) {
342 29         185 $self->{curtmpl}->add_dependency($wname, $tmpl);
343             }
344 145 100       398 my $that = $use_this ? '$this' : $tmpl->{cf_entns};
345             \ sprintf(q{%s->render_%s($CON, %s)}
346             , $that, $widget->{cf_name}
347 145         669 , $self->gen_putargs($widget, $node)
348             );
349             }
350             sub gen_putargs {
351 168     168 0 417 (my MY $self, my Widget $widget, my $node, my $delegate_vars) = @_;
352 168         623 my ($path, $body, $primary, $head, $foot) = nx($node);
353             return '' if not $delegate_vars and not $widget->{has_required_arg}
354 168 100 100     1940 and not $primary and not $body;
      100        
      100        
355 77         236 my $wname = join ":", @$path;
356 77         172 my ($posArgs, $actualNo, @argOrder);
357             my $add_arg = sub {
358 133     133   298 my ($name) = @_;
359 133 100       418 my $formal = $widget->{arg_dict}{$name} or do {
360 2         9 die $self->generror(q{Unknown arg '%s' in widget %s}, $name, $wname);
361             };
362 131 50       443 if (defined $argOrder[my $argno = $formal->argno]) {
363 0         0 die $self->generror(q{Duplicate arg '%s'}, $name);
364             } else {
365 131         275 $argOrder[$argno] = ++$actualNo;
366             }
367 131         239 $formal;
368 77         450 };
369             # primary 引数
370             my @argExpr = map {
371 77         214 $self->sync_curline($_->[NODE_LNO]), ", ", $self->add_curline(do {
  92         309  
372 92         315 my $name = argName($_);
373 92 100       256 unless (defined $name) {
374 8 50       41 defined($name = $widget->{arg_order}[$posArgs++])
375             or die $self->generror("Too many args");
376             }
377 92         220 my $formal = $add_arg->($name);
378 90 100       234 unless (my $passThruVar = passThruVar($_)) {
    100          
    100          
379 55         167 $self->as_cast_to($formal, argValue($_));
380 0         0 } elsif (my $actual = $self->find_var($passThruVar)) {
381 30 100 100     166 if ($formal->already_escaped and not $actual->already_escaped) {
382             # 受け手が escape 済みを期待しているのに、送り手がまだ escape されてないケース
383 2         29 $self->as_escaped($actual);
384             } else {
385 28         73 $self->as_lvalue($actual);
386             }
387 0 50       0 } elsif (not defined argValue($_) and defined(my $v = $formal->flag)) {
388             # フラグ立てとして扱って良い型の場合。
389 2         10 $v;
390             } else {
391 3         13 die $self->generror(q{valueless arg '%s'}, $passThruVar);
392             }
393             });
394             } @$primary;
395              
396             # element 引数
397 72 100       263 foreach my $arg (lexpand($head), $body ? $body : (), lexpand($foot)) {
398 41         114 my ($name, $expr) = @$arg[NODE_PATH, NODE_VALUE];
399 41 100       141 my $formal = $add_arg->(ref $name ? $name->[-1] : $name);
400 41         191 push @argExpr, ", ", $self->as_cast_to($formal, $expr);
401             }
402              
403             # delegate の補間と、必須引数検査
404 70         143 foreach my $i (0 .. $#{$widget->{arg_order}}) {
  70         277  
405 183 100       512 next if defined $argOrder[$i];
406 58         139 my $argName = $widget->{arg_order}[$i];
407 58 100       292 if (my $inherit = $delegate_vars->{$argName}) {
    100          
408 7         17 push @argExpr, ', '. $self->as_lvalue($inherit);
409 7         21 $argOrder[$inherit->argno] = ++$actualNo;
410             } elsif ($widget->{arg_dict}{$argName}->is_required) {
411 1         20 die $self->generror("Argument '%s' is missing", $argName);
412             }
413             }
414             sprintf q{(undef%s)[%s]}
415 182 100       1311 , join("", @argExpr), join(", ", map {defined $_ ? $_ : 0}
416 69         308 @argOrder[0 .. $#{$widget->{arg_order}}]);
  69         210  
417             }
418             sub as_lvalue {
419 878     878 0 1619 (my MY $self, my $var) = @_;
420 878         2590 my $type = $var->type;
421 878 100       4405 unless (defined $type) {
    50          
422 0         0 die $self->generror("undefined var type");
423 0         0 } elsif (my $sub = $self->can("as_lvalue_" . $type->[0])) {
424 28         76 $sub->($self, $var);
425             } else {
426 850         2468 '$'.$var->varname;
427             }
428             }
429             sub as_lvalue_html {
430 49     49 0 106 (my MY $self, my $var) = @_;
431 49         134 '$html_'.$var->varname;
432             }
433             sub as_varcall_code {
434 20     20 0 61 (my MY $self, my ($codeVar, $node)) = @_;
435 20         69 return \ sprintf q{$%1$s && $%1$s->(%2$s)}, $codeVar->varname
436             , $self->gen_putargs($codeVar->widget, $node);
437             # XXX: デフォルト body のように、引数宣言が無いケースも考慮せよ。
438             }
439             sub as_varcall_delegate {
440 3     3 0 11 (my MY $self, my ($var, $node)) = @_;
441 3         12 my Widget $delegate = $var->widget;
442 3         15 $self->ensure_generated(perl => my Template $tmpl = $delegate->{cf_folder});
443 3 50       19 my $that = $tmpl == $self->{curtmpl} ? '$this' : $tmpl->{cf_entns};
444             \ sprintf(q{%s->render_%s($CON, %s)}
445             , $that, $delegate->{cf_name}
446 3         14 , $self->gen_putargs($delegate, $node, $var->delegate_vars));
447             }
448             sub as_escaped {
449 6     6 0 17 (my MY $self, my $var) = @_;
450 6 50       27 if (my $sub = $self->can("as_escaped_" . $var->type->[0])) {
451 0         0 $sub->($self, $var);
452             } else {
453 6         23 'YATT::Lite::Util::escape($'.$var->varname.')';
454             }
455             }
456              
457             #========================================
458             sub as_cast_to {
459 139     139 0 321 (my MY $self, my $var, my $value) = @_;
460 139         387 my $type = $var->type->[0];
461 139 50       777 my $sub = $self->can("as_cast_to_$type")
462             or die $self->generror(q{Can't cast to type: %s}, $type);
463 139         482 $sub->($self, $var, $value);
464             }
465             sub as_cast_to_text {
466 82     82 0 228 (my MY $self, my ($var, $value)) = @_;
467 82 100       365 return qtext($value) unless ref $value;
468 14         63 $self->as_text(@$value);
469             }
470             sub as_cast_to_attr {
471 2     2 0 8 shift->as_cast_to_text(@_);
472             }
473             sub as_cast_to_html {
474 16     16 0 40 (my MY $self, my ($var, $value)) = @_;
475 16 100       51 unless (ref $value) {
476 4         15 $self->{curline} += numLines($value);
477 4         16 return qtext($value);
478             }
479 12         51 join '.', shift->gen_as(text => \@AS_TEXT, 1, 1, @$value);
480             }
481             sub as_cast_to_scalar {
482 6     6 0 23 (my MY $self, my ($var, $value)) = @_;
483 6 100       49 'scalar(do {'.(ref $value ? $self->as_list(@$value) : $value).'})';
484             }
485             sub as_cast_to_bool {
486 0     0 0 0 shift->as_cast_to_scalar(@_);
487             }
488             sub as_cast_to_list {
489 3     3 0 10 (my MY $self, my ($var, $value)) = @_;
490 3 100       33 '['.(ref $value ? $self->as_list(@$value) : $value).']';
491             }
492             sub as_cast_to_code {
493 32     32 0 79 (my MY $self, my ($var, $value)) = @_;
494 32         136 local $self->{curtoks} = [@$value];
495 32         113 my Widget $virtual = $var->widget;
496             local $self->{scope} = $self->mkscope
497 32   100     255 ({}, $virtual->{arg_dict} ||= {}, $self->{scope});
498 32         82 local $self->{no_last_newline} = 1;
499 32         107 q|sub {|. join('', $self->gen_getargs($virtual)
500             , $self->as_print("}"));
501             }
502             #----------------------------------------
503             sub argName {
504 145     145 0 354 my ($arg, $skip) = @_;
505 145         304 my $name = $$arg[NODE_PATH];
506 145 100 100     555 unless (wantarray and ref $name) {
    100          
507 137         373 $name;
508 0         0 } elsif (defined $skip) {
509 2         6 @{$name}[$skip .. $#$name];
  2         7  
510             } else {
511 6         22 @$name;
512             }
513             }
514 94     94 0 185 sub argValue { my $arg = shift; $$arg[NODE_VALUE] }
  94         410  
515             sub passThruVar {
516 98     98 0 181 my $arg = shift;
517 98 100       439 if ($arg->[NODE_TYPE] == TYPE_ATT_NAMEONLY) {
    100          
518 33         163 $$arg[NODE_PATH]
519             } elsif ($arg->[NODE_TYPE] == TYPE_ATT_BARENAME) {
520 8         47 $$arg[NODE_VALUE]
521             }
522             }
523             #========================================
524             sub from_pi {
525 37     37 0 91 (my MY $self, my $node) = @_;
526             # pi の ns 毎の役割を拡張可能に
527 37 50       294 if (my $sub = $self->can("pi_of_" . $node->[NODE_PATH][0])) {
528 0         0 return $sub->($self, $node);
529             }
530 37         131 $self->sync_curline($node->[NODE_LNO]);
531 37         127 my @body = nx($node, 1);
532 37         75 my ($fmt, $is_statement) = do {
533 37 100       177 unless ($body[0] =~ s/^=+//) {
    100          
534 25         57 (q{%s}, 1);
535 0         0 } elsif (length $& >= 3) {
536 6         19 q{do {%s}};
537             } else {
538 6         24 q{YATT::Lite::Util::escape(do {%s})};
539             }
540             };
541 37         160 my $expr = join '', $self->as_list(@body);
542 37 100       212 return \ "" unless $expr =~ /\S/;
543 36         117 my $script = sprintf $fmt, $expr;
544 36 100       128 $is_statement ? \ $script : $script;
545             }
546             #========================================
547       0 0   sub from_lineinfo { }
548             sub from_comment {
549 2     2 0 5 (my MY $self, my $node) = @_;
550 2         7 (undef, my ($nlines, $body)) = nx($node); # XXX: ok?
551 2         5 $self->{curline} += $nlines;
552 2         7 return \ ("\n" x $nlines);
553             }
554             sub from_lcmsg {
555 3     3 0 8 (my MY $self, my $node) = @_;
556 3         10 my ($path, $body) = nx($node);
557             # $body is list of tokenlist.
558 3         12 my $place = $self->{curtmpl}->fake_filename . ":" . $node->[NODE_LNO];
559              
560             # XXX: builtin xgettext
561 3 100 66     17 if (@$body >= 2 or @$path >= 2) {
562             # ngettext
563 1         4 my ($uniq, $args, $numexpr) = ({}, []);
564             my ($msgid, @plural) = map {
565 1         3 scalar $self->gen_lcmsg($node, $_, $uniq, $args, \$numexpr);
  2         8  
566             } @$body;
567 1 50       5 if (my $sub = $self->{cf_lcmsg_sink}) {
568 1         6 $sub->($place, $msgid, \@plural, $args);
569             }
570             sprintf q{sprintf($CON->ngettext(%s, %s), %s)}
571 1         10 , join(", ", map {qtext($_)} ($msgid, @plural))
  2         7  
572             , $numexpr, join(", ", @$args);
573             } else {
574 2         11 my ($msgid, @args) = $self->gen_lcmsg($node, $body->[0]);
575 2 50       9 if (my $sub = $self->{cf_lcmsg_sink}) {
576 2         7 $sub->($place, $msgid, undef, \@args);
577             }
578 2         17 sprintf q{sprintf($CON->gettext(%s), %s)}
579             , qtext($msgid), join(", ", @args);
580             }
581             }
582             sub gen_lcmsg {
583 4     4 0 10 (my MY $self, my ($node, $list, $uniq, $args, $ref_numeric)) = @_;
584 4         7 my ($msgid, $vspec) = ("");
585 4 100 33     54 if (@$list >= 2 and not ref $list->[0] and not ref $list->[-1]
      33        
      66        
      66        
586             and $list->[0] =~ /^\n+$/ and $list->[-1] =~ /^\n+$/) {
587 2         5 shift @$list; pop @$list;
  2         5  
588 2 50 33     10 if (@$list and not ref $list->[0]) {
589 2         7 $list->[0] =~ s/^\s+//;
590             }
591             }
592 4         13 foreach my $item (@$list) {
593 12 50       64 unless (ref $item) {
    50          
    100          
594             # XXX: How about backslash?
595 7         20 (my $cp = $item) =~ s/%/%%/g;
596 7         16 $msgid .= $cp;
597 0         0 } elsif ($item->[NODE_TYPE] != TYPE_ENTITY) {
598 0         0 die "SYNERR";
599 0 50       0 } elsif (ref ($vspec = $item->[NODE_BODY]) ne 'ARRAY'
600             || $vspec->[0] ne 'var') {
601             # || @$vspec != 2
602 0         0 die "SYNERR";
603             } else {
604 5         11 my $name = $vspec->[1];
605 5 50       15 my $var = $self->find_var($name)
606             or die $self->generror(q{No such variable '%s'}, $name);
607 5 100       15 unless ($uniq->{$name}) {
608 4         16 push @$args, $self->as_escaped($var);
609 4         15 $uniq->{$name} = 1 + keys %$uniq;
610             }
611 5 100       14 my $argno = $ref_numeric ? $uniq->{$name} . '$' : '';
612             # XXX: type==value is alias of scalar.
613 5 100 100     60 if ($ref_numeric and $var->type->[0] eq 'scalar') {
614 1         3 $msgid .= "%${argno}d"; # XXX: format selection... but how? from entity?
615 1 50       5 if ($$ref_numeric) {
616 0         0 die "SYNERR";
617             }
618 1         3 $$ref_numeric = $self->as_lvalue($var);
619             } else {
620 4         14 $msgid .= "%${argno}s";
621             }
622             }
623             }
624 4         9 $msgid =~ s/\r//g;
625             # XXX: Unfortunately, this is not good for multiline message.
626 4 100       19 wantarray ? ($msgid, lexpand($args)) : $msgid;
627             }
628              
629             sub from_elematt {
630 0     0 0 0 (my MY $self, my $node) = @_;
631             # <:yatt:elematt>.... は NOP へ。
632 0         0 return \ "";
633             }
634             sub from_entity {
635 355     355 0 794 (my MY $self, my $node) = @_;
636 355         1054 (undef, my @pipe) = nx($node);
637             # XXX: expand のように全体に作用するものも有るから、これも現在の式を渡す方式にすべき。
638             # 受け手が有るかどうかで式の生成方式も変わる?なら token リスト削りが良いか。
639 355         1372 $self->gen_entpath($self->{needs_escaping}, @pipe);
640             }
641              
642             # XXX: lxnest を caller が呼ぶ必要が有る...が、それって良いことなのか...
643             sub gen_entpath {
644 830     830 0 1813 (my MY $self, my ($escape_now)) = splice @_, 0, 2;
645 830 50       1871 return '' unless @_;
646 830         1770 local $self->{needs_escaping} = 0;
647 830 100 100     5431 if (@_ == 1 and $_[0][0] eq 'call'
      100        
648             and my $macro = $self->can("entmacro_$_[0][1]")) {
649 108         302 return $macro->($self, $_[0]);
650             }
651             # XXX: path の先頭と以後は分けないと! as_head, as_rest?
652             my @result = map {
653 722         1434 my ($type, @rest) = @$_;
  760         1779  
654 760 50       3158 unless (my $sub = $self->can("as_expr_$type")) {
655 0         0 die $self->generror("unknown entity item %s", terse_dump($type));
656             } else {
657 760         2063 $sub->($self, \$escape_now, @rest);
658             }
659             } @_;
660 713 50       1888 return '' unless @result;
661 713 100       1751 my $result = @result > 1 ? join("->", @result) : $result[0];
662             # XXX: これだと逆に、 html 型が困る。
663 713 100 100     2244 if (not $escape_now or ref $result) {
664 526         1575 $result;
665             } else {
666 187         922 sprintf(q{YATT::Lite::Util::escape(%s)}, $result);
667             }
668             }
669             sub gen_entlist {
670 262     262 0 626 (my MY $self, my ($escape_now)) = splice @_, 0, 2;
671             my @list = map {
672 262         546 $self->gen_entpath($escape_now, lxnest($_))
  439         1175  
673             } @_;
674 262 100       1282 wantarray ? @list : join ", ", @list;
675             }
676             sub as_expr_var {
677 230     230 0 580 (my MY $self, my ($esc_later, $name)) = @_;
678 230 100       751 my $var = $self->find_var($name)
679             or die $self->generror(q{No such variable '%s'}, $name);
680 222 100       763 if (my $sub = $self->can("as_expr_var_" . $var->type->[0])) {
681 26         83 $sub->($self, $esc_later, $var, $name);
682             } else {
683 196         527 $self->as_lvalue($var);
684             }
685             }
686             sub as_expr_var_html {
687 21     21 0 56 (my MY $self, my ($esc_later, $var, $name)) = @_;
688 21         43 $$esc_later = 0;
689 21         58 $self->as_lvalue_html($var);
690             }
691             sub as_expr_var_attr {
692 5     5 0 11 (my MY $self, my ($esc_later, $var, $name)) = @_;
693             # $$esc_later = 0;
694 5         9 (undef, my $attname) = @{$var->type};
  5         13  
695 5   33     43 sprintf(q{YATT::Lite::Util::named_attr('%s', $%s)}
696             , $attname // $name, $name);
697             }
698             sub as_expr_call {
699 150     150 0 425 (my MY $self, my ($esc_later, $name)) = splice @_, 0, 3;
700             # XXX: 受け側が print か、それとも一般の式か。 print なら \ すべき。
701             # entns があるか、find_code_var か。さもなければエラーよね。
702 150 100       575 if (my $var = $self->find_callable_var($name)) {
703             # code 引数の中の引数のデフォルト値の中に、改行が有ったら??
704             # XXX: body の引数宣言が無い場合に は、ちゃんと呼び出せるか?
705 11         51 return $self->as_expr_call_var($var, $name, @_);
706             }
707              
708 139         509 my Template $tmpl = $self->{curtmpl};
709 139 100       2228 unless ($tmpl->{cf_entns}->can("entity_$name")) {
710             die $self->generror(q!No such entity in namespace "%s": %s!
711 1         4 , $tmpl->{cf_entns}, $name);
712             }
713 138         552 my $call = sprintf '$this->entity_%s(%s)', $name
714             , scalar $self->gen_entlist(undef, @_);
715 138         521 $call;
716             }
717             sub as_expr_call_var {
718 11     11 0 33 (my MY $self, my ($var, $name, @args)) = @_;
719 11 100       40 if (my $sub = $self->can("as_expr_call_var_" . $var->type->[0])) {
720 1         10 $sub->($self, $var, $name, @args);
721             } else {
722 10         47 \ sprintf q{$%1$s && $%1$s->(%2$s)}, $name
723             , scalar $self->gen_entlist(undef, @args);
724             }
725             }
726             sub as_expr_call_var_attr {
727 1     1 0 5 (my MY $self, my ($var, $name, @args)) = @_;
728 1         3 (undef, my $attname) = @{$var->type};
  1         5  
729 1   33     9 sprintf q|YATT::Lite::Util::named_attr('%s', %s)|
730             , $attname // $name
731             , join ", ", '$'.$name, $self->gen_entlist(undef, @args);
732             }
733             sub as_expr_invoke {
734 5     5 0 18 (my MY $self, my ($esc_later, $name)) = splice @_, 0, 3;
735 5         37 sprintf '%s(%s)', $name
736             , scalar $self->gen_entlist(undef, @_);
737             }
738              
739             sub as_expr_expr {
740 21     21 0 53 (my MY $self, my ($esc_later, $expr)) = @_;
741 21         69 $expr;
742             }
743             sub as_expr_array {
744 5     5 0 12 (my MY $self, my ($esc_later)) = splice @_, 0, 2;
745 5         22 '['.$self->gen_entlist(undef, @_).']';
746             }
747             sub as_expr_hash {
748 0     0 0 0 (my MY $self, my ($esc_later)) = splice @_, 0, 2;
749 0         0 '{'.$self->gen_entlist(undef, @_).'}';
750             }
751             sub as_expr_aref {
752 21     21 0 54 (my MY $self, my ($esc_later, $node)) = @_;
753 21         61 '['.$self->gen_entpath(undef, lxnest($node)).']';
754             }
755             sub as_expr_href {
756 10     10 0 31 (my MY $self, my ($esc_later, $node)) = @_;
757 10         41 '{'.$self->gen_entpath(undef, lxnest($node)).'}';
758             }
759             sub as_expr_prop {
760 2     2 0 5 (my MY $self, my ($esc_later, $name)) = @_;
761 2 50       18 if ($name =~ /^\w+$/) {
762 2         10 "{$name}"
763             } else {
764 0         0 '{'.qtext($name).'}';
765             }
766             }
767             sub as_expr_text {
768 316     316 0 615 (my MY $self, my ($esc_later, $expr)) = @_;
769 316         762 qqvalue($expr);
770             }
771             #========================================
772             }
773              
774             sub make_arg_spec {
775 30     30 0 95 my ($pack, $dict, $order) = splice @_, 0, 3;
776 30         72 foreach my $name (@_) {
777 75         174 $dict->{$name} = @$order;
778 75         168 push @$order, $name;
779             }
780             }
781              
782             sub feed_arg_spec {
783 23     23 0 70 (my MY $trans, my ($args, $arg_dict, $arg_order)) = splice @_, 0, 4;
784 23         50 my ($found, $nth);
785 23         71 foreach my $arg (lexpand($args)) {
786 27         84 my ($name, @ext) = argName($arg); # XXX: は?
787 27 100       80 unless (defined $name) {
788 7 50       35 $name = $arg_order->[$nth++]
789             or die $trans->generror($arg, "Too many args");
790             }
791 27 50       84 defined (my $argno = $arg_dict->{$name})
792             or die $trans->generror($arg, "Unknown arg '%s'", $name);
793              
794 27         51 $_[$argno] = $arg;
795 27         57 $found++;
796             }
797 23         73 $found;
798             }
799              
800             {
801             MY->make_arg_spec(\ my %args, \ my @args, qw(if unless));
802             sub macro_if {
803 9     9 0 19 (my MY $self, my $node) = @_;
804 9         40 my ($path, $body, $primary, $head, $foot) = nx($node);
805 9         26 my @arms = do {
806 9 50       45 $self->feed_arg_spec($primary, \%args, \@args
807             , my ($if, $unless))
808             or die $self->generror("Not enough arguments!");
809 9         23 my ($kw, $cond) = do {
810 9 50       25 if ($if) { (if => $if) }
  9 0       48  
811 0         0 elsif ($unless) { (unless => $unless) }
812 0         0 else { die "??" }
813             };
814 9         40 ["$kw (%s) ", $cond->[NODE_VALUE], lexpand($body->[NODE_VALUE])];
815             };
816              
817             # いかん、 cond を生成するなら、body も生成しておかないと、行番号が困る。
818              
819 9         31 foreach my $arg (lexpand($foot)) {
820 6 50       21 if ($arg->[NODE_PATH][-1] eq 'else') {
821 6         25 $self->feed_arg_spec($arg->[NODE_ATTLIST], \%args, \@args
822             , my ($if, $unless));
823 6         13 my ($fmt, $guard) = do {
824 6 100       22 if ($if) { (q{elsif (%s) }, $if->[NODE_VALUE]) }
  2 50       6  
825 0         0 elsif ($unless) { (q{elsif (not %s) }, $unless->[NODE_VALUE]) }
826 4         12 else { (q{else }, undef) }
827             };
828 6         20 push @arms, [$fmt, $guard, lexpand($arg->[NODE_VALUE])]
829             } else {
830 0         0 push @{$arms[-1]}, lexpand($arg->[NODE_VALUE]);
  0         0  
831             }
832             }
833 9         41 local $self->{scope} = $self->mkscope({}, $self->{scope});
834             my @expr = map {
835 9         26 my ($fmt, $guard, @body) = @$_;
  15         47  
836 15         43 local $self->{curtoks} = [@body];
837 15 100       67 (defined $guard
838             ? sprintf($fmt, join "", $self->as_list(lexpand($guard))) : $fmt)
839             .'{'.$self->cut_next_nl.$self->as_print('}');
840             } @arms;
841 9         33 \ join "", @expr, $self->cut_next_nl;
842             }
843             }
844              
845             {
846             sub macro_my {
847 16     16 0 36 (my MY $self, my $node) = @_;
848 16         60 my ($path, $body, $primary, $head, $foot) = nx($node);
849              
850 16 100 66     84 my $has_body = $body && @$body ? 1 : 0;
851             my $adder = sub {
852 26     26   60 my ($default_type, $arg, $valNode, $skip) = @_;
853 26         79 my ($name, $typename) = argName($arg, $skip);
854 26 100       97 if (my $oldvar = $self->find_var($name)) {
855 1   50     7 die $self->generror("Conflicting variable '%s'"
856             ." (previously defined at line %s)"
857             , $name, $oldvar->lineno // '(unknown)');
858             }
859 25   66     110 $typename ||= $default_type;
860 25 100       184 if (my $sub = $self->can("_macro_my_$typename")) {
861 3         12 $sub->($self, $node, $name, $valNode);
862             } else {
863 22 50       87 my $var = $self->{scope}[0]{$name}
864             = $self->mkvar_at(undef, $typename, $name)
865             or die $self->generror("Unknown type '%s' for variable '%s'"
866             , $typename, $name);
867             # typename == source の時が問題だ。
868 22         72 my $expr = 'my '.$self->as_lvalue($var);
869 22         62 my $value = argValue($valNode);
870 22 100       97 $expr .= $value ? (' = '.$self->as_cast_to($var, $value)) : ';';
871             }
872 16         102 };
873 16         33 my @assign;
874 16         46 foreach my $arg (@{$primary}[0 .. $#$primary-$has_body]) {
  16         45  
875 17         46 push @assign, $adder->(text => $arg, $arg);
876             }
877 15 100       56 if ($has_body) {
878 6         16 my $arg = $primary->[-1];
879             # XXX: ここは統合できるはず。ただし、NESTED の時に name が無いことを確認すべき。
880 6 100       18 if ($$arg[NODE_TYPE] == TYPE_ATT_NESTED) {
881 1         6 foreach my $each (nx($arg, 1)) {
882 2         6 push @assign, $adder->(html => $each, $body);
883             }
884             } else {
885 5         19 push @assign, $adder->(html => $arg, $body);
886             }
887             }
888 15         37 foreach my $arg (map {lexpand($_)} $head, $foot) {
  30         79  
889 2         7 push @assign, $adder->(text => $arg, $arg, 1); # Skip leading :yatt:
890             }
891 15         159 \ join "; ", @assign;
892             }
893             sub _macro_my_code {
894 2     2   7 (my MY $self, my ($node, $name, $valNode)) = @_;
895 2         9 my $var = $self->{scope}[0]{$name} = $self->mkvar_at(undef, code => $name);
896 2         6 local $self->{curtoks} = [lexpand(argValue($valNode))];
897 2         9 'my '.$self->as_lvalue($var).' = '.q|sub {| . $self->as_print('}');
898             }
899             sub _macro_my_source {
900 1     1   5 (my MY $self, my ($node, $name, $valNode)) = @_;
901 1         6 my $var = $self->{scope}[0]{$name} = $self->mkvar_at(undef, text => $name);
902             'my '.$self->as_lvalue($var).' = '
903 2         8 .join(q|."\n".|, map {qtext($_)}
904 1         6 split /\n/, $self->{curtmpl}->node_body_source($node));
905             }
906              
907             sub macro_block {
908 3     3 0 6 (my MY $self, my $node) = @_;
909 3         11 my ($path, $body, $primary, $head, $foot) = nx($node);
910 3         7 $self->macro_scoped_block_of_tokens(+{}, @{argValue($body)});
  3         10  
911             }
912              
913             sub macro_scoped_block_of_tokens {
914 3     3 0 13 (my MY $self, my ($scope, @tokens)) = @_;
915 3         10 local $self->{scope} = $self->mkscope($scope, $self->{scope});
916 3         8 local $self->{curtoks} = \@tokens;
917 3         10 \ ('{'.$self->as_print('}'));
918             }
919             }
920              
921             {
922             MY->make_arg_spec(\ my %args, \ my @args, qw(list my nth));
923             sub macro_foreach {
924 8     8 0 25 (my MY $self, my ($node, $opts)) = @_;
925 8         38 my ($path, $body, $primary, $head, $foot) = nx($node);
926 8 50       56 $self->feed_arg_spec($primary, \%args, \@args
927             , my ($list, $my, $nth))
928             or die $self->generror("Not enough arguments!");
929              
930 8         31 my ($prologue, $continue, $epilogue) = ('', '', '');
931              
932 8 50       25 unless (defined $list) {
933 0         0 die $self->generror("no list= is given");
934             }
935              
936 8         16 my %local;
937 8         15 my $loopvar = do {
938 8 100       20 if ($my) {
939 7         23 my ($x, @type) = lexpand($my->[NODE_PATH]);
940 7         16 my $varname = $my->[NODE_VALUE];
941 7   50     59 $local{$varname} = $self->mkvar_at(undef, $type[0] || '' => $varname);
942 7         22 'my $' . $varname;
943             } else {
944             # _ は? entity 自体に処理させるか…
945 1         2 ''
946             }
947             };
948              
949 8         17 my ($nth_var, @nth_type) = do {
950 8 100 66     39 if ($nth and my $vn = $nth->[NODE_VALUE]) {
951 1         5 my ($x, @t) = lexpand($nth->[NODE_PATH]);
952 1 50       7 if ($vn =~ /^(\w+)$/) {
953 1         5 ($vn, @t);
954             } else {
955 0         0 die $self->generror("Invalid nth var: %s", $nth);
956             }
957             }
958             };
959 8 100       28 if ($nth_var) {
960 1   50     8 $local{$nth_var} = $self->mkvar_at(undef, $nth_type[0] || '' => $nth_var);
961              
962 1         4 $prologue .= sprintf q{ my $%s = 1;}, $nth_var;
963 1         4 $continue .= sprintf q{ $%s++;}, $nth_var;
964             }
965              
966 8         21 my $fmt = q|{%4$s; foreach %1$s (%2$s) %3$s continue {%5$s} %6$s}|;
967 8         16 my $listexpr = do {
968 8 50       32 unless (my $passThruVarName = passThruVar($list)) {
    100          
969 2         10 $self->as_list(lexpand($list->[NODE_VALUE]));
970 0         0 } elsif (my $found_var = $self->find_var($passThruVarName)) {
971 6 100       41 unless ($found_var->is_type('list')) {
972 1         18 die $self->generror(q{%s - %s should be list type.}
973             , join(":", @$path), $passThruVarName);
974             }
975 5         15 '@'.$self->as_lvalue($found_var);
976             } else {
977 0         0 die $self->generror("Unknown list=");
978             }
979             };
980              
981 7         13 local $self->{curtoks} = [@{argValue($body)}];
  7         22  
982 7         34 local $self->{scope} = $self->mkscope(\%local, $self->{scope});
983 7         28 my $statements = '{'.$self->as_print('}');
984              
985 7 50 33     40 if ($opts and $opts->{fragment}) {
986 0         0 ($fmt, $loopvar, $listexpr, $statements
987             , $prologue, $continue, $epilogue);
988             } else {
989 7         63 \ sprintf $fmt, $loopvar, $listexpr, $statements
990             , $prologue, $continue, $epilogue;
991             }
992             }
993             }
994              
995             sub entx {
996 108     108 0 253 my ($node) = @_;
997 108         216 @{$node}[2..$#$node];
  108         416  
998             }
999              
1000             sub entmacro_if {
1001 99     99 0 213 (my MY $self, my $node) = @_;
1002 99         259 my ($cond, $then, $else) = $self->gen_entlist(undef, entx($node));
1003             sprintf q|do {(%s) ? (%s) : (%s)}|
1004 99 50 50     306 , map {ref $_ ? $$_ : $_} $cond, $then, $else || q{''};
  297         1041  
1005             }
1006              
1007             sub entmacro_ifeq {
1008 0     0 0 0 (my MY $self, my $node) = @_;
1009 0         0 my ($val, $what, $then, $else) = $self->gen_entlist(undef, entx($node));
1010             sprintf q|do {((%s // '') eq (%s // '')) ? (%s) : (%s)}|
1011 0 0 0     0 , map {ref $_ ? $$_ : $_} $val, $what, $then, $else || q{''};
  0         0  
1012             }
1013              
1014             sub entmacro_value_checked {
1015 0     0 0 0 (my MY $self, my $node) = @_;
1016 0         0 my (@list) = $self->gen_entlist(undef, entx($node));
1017 0 0       0 unless (@list == 2) {
1018 0         0 die $self->generror("Invalid number of args: value_checked(VALUE, HASH)");
1019             }
1020             sprintf q|YATT::Lite::Util::value_checked(%s)|
1021 0 0       0 , join ", ", map {ref $_ ? $$_ : $_} @list;
  0         0  
1022             }
1023              
1024             sub entmacro_value_selected {
1025 0     0 0 0 (my MY $self, my $node) = @_;
1026 0         0 my (@list) = $self->gen_entlist(undef, entx($node));
1027 0 0       0 unless (@list == 2) {
1028 0         0 die $self->generror("Invalid number of args: value_selected(VALUE, HASH)");
1029             }
1030             sprintf q|YATT::Lite::Util::value_selected(%s)|
1031 0 0       0 , join ", ", map {ref $_ ? $$_ : $_} @list;
  0         0  
1032             }
1033              
1034             sub entmacro_lexpand {
1035 5     5 0 14 (my MY $self, my $node) = @_;
1036 5         28 q|@{|.$self->gen_entpath(undef, map {lxnest($_)} entx($node)).q|}|;
  5         17  
1037             }
1038              
1039             sub entmacro_render {
1040 4     4 0 12 (my MY $self, my $node) = @_;
1041 4         20 my ($wname, @expr) = $self->gen_entlist(undef, entx($node));
1042 4         31 \ sprintf q{YATT::Lite::Util::safe_render($this, $CON, %s, %s)}
1043             , $wname, join(", ", @expr);
1044             }
1045              
1046             sub entmacro_dispatch_all {
1047 0     0 0   (my MY $self, my $node) = @_;
1048 0           my ($prefix, $nargs, @list) = $self->gen_entlist(undef, entx($node));
1049 0           \ sprintf q{YATT::Lite::Util::dispatch_all($this, $CON, %s, %s, %s)}
1050             , $prefix, $nargs, join(", ", @list);
1051             }
1052              
1053             sub entmacro_dispatch_one {
1054 0     0 0   (my MY $self, my $node) = @_;
1055 0           my ($prefix, $nargs, @list) = $self->gen_entlist(undef, entx($node));
1056 0           \ sprintf q{YATT::Lite::Util::dispatch_one($this, $CON, %s, %s, %s)}
1057             , $prefix, $nargs, join(", ", @list);
1058             }
1059              
1060 15     15   210 use YATT::Lite::Breakpoint qw(break_load_cgen break_cgen);
  15         43  
  15         1626  
1061             break_load_cgen();
1062              
1063             1;