File Coverage

blib/lib/YATT/Lite/CGen/Perl.pm
Criterion Covered Total %
statement 510 566 90.1
branch 221 280 78.9
condition 80 120 66.6
subroutine 77 86 89.5
pod 0 72 0.0
total 888 1124 79.0


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