File Coverage

blib/lib/Anarres/Mud/Driver/Compiler/Generate.pm
Criterion Covered Total %
statement 70 217 32.2
branch 4 34 11.7
condition 0 6 0.0
subroutine 17 46 36.9
pod 0 2 0.0
total 91 305 29.8


line stmt bran cond sub pod time code
1             package Anarres::Mud::Driver::Compiler::Generate;
2              
3 3     3   1616 use strict;
  3         8  
  3         122  
4 3     3   15 use Carp qw(:DEFAULT cluck);
  3         6  
  3         467  
5 3     3   15 use Exporter;
  3         5  
  3         88  
6 3     3   16 use Data::Dumper;
  3         3  
  3         128  
7 3     3   15 use String::Escape qw(quote printable);
  3         7  
  3         128  
8 3     3   17 use Anarres::Mud::Driver::Compiler::Type;
  3         5  
  3         106  
9 3     3   15 use Anarres::Mud::Driver::Compiler::Node qw(@NODETYPES);
  3         4  
  3         244  
10 3     3   16 use Anarres::Mud::Driver::Compiler::Check qw(:flags);
  3         6  
  3         2151  
11              
12             push(@Anarres::Mud::Driver::Compiler::Node::ISA, __PACKAGE__);
13              
14             my %ASSERTTABLE = (
15             IntAssert => '+do { my ($__a) = ((A)); ' .
16             'die "Not integer at XXX" if ref($__a); ' .
17             '$__a; }',
18             StrAssert => '+do { my ($__a) = ((A)); ' .
19             'die "Not string at XXX" if ref($__a); ' .
20             '$__a; }',
21             ArrAssert => '+do { my ($__a) = ((A)); ' .
22             'die "Not array at XXX" if ref($__a) ne "ARRAY"; '.
23             '$__a; }',
24             MapAssert => '+do { my ($__a) = ((A)); ' .
25             'die "Not mapping at XXX" if ref($__a) ne "HASH"; '.
26             '$__a; }',
27             ClsAssert => '+do { my ($__a) = ((A)); ' .
28             'die "Not closure at XXX" if ref($__a) ne "CODE"; '.
29             '$__a; }',
30             ObjAssert => '+do { my ($__a) = ((A)); ' . # XXX Fixme
31             'die "Not object at XXX" if ref($__a) !~ /::/; ' .
32             '$__a; }',
33             );
34              
35             # If we trap the relevant error messages from Perl and accept that
36             # we are not going to get an error message on (array + 1) - we
37             # just get a pointer increment, then we can just do this.
38             my %ASSERTTABLE_NOOP = (
39             IntAssert => 'A',
40             StrAssert => 'A',
41             ArrAssert => 'A',
42             MapAssert => 'A',
43             ClsAssert => 'A',
44             ObjAssert => 'A',
45             );
46              
47             my %OPCODETABLE = (
48             # Can we tell the difference between strings and ints here?
49             # DConway says this tells us if it's an int:
50             # ($s_ref eq "" && defined $s_val && (~$s_val&$s_val) eq 0)
51              
52             StmtNull => '',
53              
54             Nil => 'undef',
55              
56             %ASSERTTABLE_NOOP,
57              
58             Postinc => '(A)++',
59             Postdec => '(A)--',
60             Preinc => '++(A)',
61             Predec => '--(A)',
62             Unot => '!(A)',
63             Tilde => '~(A)',
64             Plus => '+(A)',
65             Minus => '-(A)',
66              
67             IntAdd => '(A) + (B)',
68             IntSub => '(A) - (B)',
69             IntMul => '(A) * (B)',
70             IntDiv => '(A) / (B)',
71             IntMod => '(A) % (B)',
72              
73             IntLsh => '(A) << (B)',
74             IntRsh => '(A) >> (B)',
75              
76             IntOr => '(A) | (B)',
77             IntAnd => '(A) & (B)',
78             IntXor => '(A) ^ (B)',
79              
80             IntAddEq => '(A) += (B)',
81             IntSubEq => '(A) -= (B)',
82             IntMulEq => '(A) *= (B)',
83             IntDivEq => '(A) /= (B)',
84             IntModEq => '(A) %= (B)',
85              
86             IntLshEq => '(A) <<= (B)',
87             IntRshEq => '(A) >>= (B)',
88              
89             IntOrEq => '(A) |= (B)',
90             IntAndEq => '(A) &= (B)',
91             IntXorEq => '(A) ^= (B)',
92              
93             StrAdd => '(A) . (B)',
94             StrMul => '(A) x (B)',
95              
96             StrAddEq => '(A) .= (B)',
97             StrMulEq => '(A) x= (B)',
98              
99             IntEq => '(A) == (B)',
100             IntNe => '(A) != (B)',
101             IntLt => '(A) < (B)',
102             IntGt => '(A) > (B)',
103             IntLe => '(A) <= (B)',
104             IntGe => '(A) >= (B)',
105              
106             StrEq => '(A) eq (B)',
107             StrNe => '(A) ne (B)',
108             StrLt => '(A) lt (B)',
109             StrGt => '(A) gt (B)',
110             StrLe => '(A) le (B)',
111             StrGe => '(A) ge (B)',
112              
113             ArrEq => '(A) == (B)',
114             ArrNe => '(A) != (B)',
115              
116             MapEq => '(A) == (B)',
117             MapNe => '(A) != (B)',
118              
119             ObjEq => '(A) == (B)',
120             ObjNe => '(A) != (B)',
121              
122             LogOr => '(A) || (B)',
123             LogAnd => '(A) && (B)',
124              
125             LogOrEq => '(A) ||= (B)',
126             LogAndEq => '(A) &&= (B)',
127              
128             ExpComma => '(A), (B)', # XXX Wrong?
129             ExpCond => '(A) ? (B) : (C)',
130              
131             New => '{ }', # XXX Initialise to class?
132             Member => '(A)->{_B_}',
133              
134             ArrIndex => '(A)->[B]',
135             MapIndex => '(A)->{B}',
136             StrIndex => 'substr((A), (B), 1)', # XXX Wrong! Use Core XSUB
137              
138             ArrRangeLL => '[ (A)->[(B)..(C)] ]',
139             ArrRangeRL => '[ splice(@{[ @{A}, undef ]}, -(B), (C)) ]',
140             ArrRangeLR => '[ splice(@{[ @{A}, undef ]}, (B), -(C)) ]',
141             ArrRangeRR => '[ splice(@{[ @{A}, undef ]}, -(B), -(C)) ]',
142              
143             # eval the args once outside scope of $__* vars
144             # XXX Use the XSUB in Core
145             StrRangeCstLL => 'substr(A, B, (C) - (B))',
146             StrRangeCstLR => 'substr(A, B, (B) - (C))',
147             StrRangeCstRL => 'substr(A, -(B), (C) - (B))',
148             StrRangeCstRR => 'substr(A, -(B), (B) - (C))',
149              
150             StrRangeVarLL => 'do { my ($__a, $__b, $__c) = ((A), (B), (C)); '.
151             'substr($__a, $__b, ($__c - $__b)) }',
152             StrRangeVarLR => 'do { my ($__a, $__b, $__c) = ((A), (B), (C)); '.
153             'substr($__a, $__b, ($__b - $__c)) }',
154             StrRangeVarRL => 'do { my ($__a, $__b, $__c) = ((A), (B), (C)); '.
155             'substr($__a, - $__b, ($__c - $__b)) }',
156             StrRangeVarRR => 'do { my ($__a, $__b, $__c) = ((A), (B), (C)); '.
157             'substr($__a, - $__b, ($__b - $__c)) }',
158              
159             ArrAdd => '[ @{A}, @{B} ]',
160             ArrSub => 'do { my %__a = map { $_ => 1 } @{B}; ' .
161             '[ grep { ! $__a{$_} } @{ A } ] }',
162              
163             MapAdd => '{ %{A}, %{B} }',
164              
165             Assign => 'A = B',
166             Catch => 'do { eval { A; }, $@; }',
167              
168             StmtReturn => 'return A;',
169             StmtContinue => 'next;',
170              
171             # We can add extra braces around statement|block tokens
172             # This lot are all strictly cheating anyway! If this works ...
173             StmtExp => 'A;',
174             # Should we promote_to_block() B in these statements?
175             # Bear in mind what happens if we do an empty block...?
176             StmtDo => 'do { B } while (A);',
177             StmtWhile => 'while (A) { B }',
178             StmtFor => 'for (A; B; C) D',
179             StmtForeachArr => 'foreach my A (@{ C }) D',
180             StmtForeachMap => 'foreach my A (keys %{ C }) D', # XXX FIXME: B
181             StmtTry => 'eval A; if ($@) { my B = $@; C; }',
182             # This uses blocks
183             StmtCatch => 'eval A ;', # A MudOS hack
184              
185             # This NOGEN business is really developer support and can be removed
186             map { $_ => 'NOGEN' } qw(
187             Variable
188             Index Range
189             Lsh Rsh
190             Add Sub Mul Div Mod
191             Eq Ne Lt Gt Le Ge Or
192             And Xor
193            
194             AddEq SubEq DivEq MulEq ModEq
195             AndEq OrEq XorEq
196             LshEq RshEq
197              
198             StmtForeach
199             ),
200             );
201              
202             # XXX For the purposes of things like Member, I need to be able to
203             # insert both expanded and nonexpanded versions of tokens.
204             # So I need to be able to insert "A", _A_ and @A@ tokens, for example.
205              
206             sub gensub {
207 4     4 0 10 my ($self, $name, $code) = @_;
208              
209 4 50       19 confess "No code template for opcode '$name'" unless defined $code;
210              
211 4         18 foreach ('A'..'F') { # Say ...
212 24         36 my $arg = ord($_) - ord('A');
213             # XXX This 'quote' routine doesn't necessarily quote
214             # appropriately.
215 24         186 $code =~ s/"$_"/' . quote(\$self->value($arg)) . '/g;
216 24         142 $code =~ s/\b_$_\_\b/' . \$self->value($arg) . '/g;
217 24         221 $code =~ s/\b$_\b/' . \$self->value($arg)->generate(\@_) . '/g;
218             }
219              
220 4         16 $code = qq{ sub (\$) { my \$self = shift; return '$code'; } };
221             # Remove empty concatenations - careful with the templates
222 4         12 $code =~ s/'' \. //g;
223 4         9 $code =~ s/ \. ''//g;
224              
225             # print "$name becomes $code\n";
226 4     0   1807 my $subref = eval $code;
  0         0  
  0         0  
  1         3  
  1         7  
227 4 50       17 die $@ if $@;
228 4         20 return $subref;
229             }
230              
231             # "Refactor", I hear you say?
232             # This needs a magic token for line number...
233             sub generate ($) {
234 1     1 0 3 my $self = shift;
235              
236 1         4 my $name = $self->opcode;
237             # print "Finding code for $name\n";
238 1         4 my $code = $OPCODETABLE{$name};
239 1 50       5 return "GEN($name)" unless defined $code;
240              
241             # This is mostly for debugging. It can be safely removed.
242 1 50       4 if ($code eq 'NOGEN') {
243 0         0 print "XXX Attempt to generate NOGEN opcode $name\n";
244 0         0 return "GEN($name)";
245             }
246              
247 1         9 my $subref = $self->gensub($name, $code);
248              
249             {
250             # Backpatch our original package.
251 3     3   18 no strict qw(refs);
  3         6  
  3         246  
  1         3  
252 1         3 *{ ref($self) . '::generate' } = $subref;
  1         8  
253             }
254              
255 1         31 return $subref->($self, @_);
256             }
257              
258             {
259             package Anarres::Mud::Driver::Compiler::Node::String;
260 3     3   17 use String::Escape qw(quote printable);
  3         18  
  3         2672  
261             sub generate {
262 0     0     my $str = printable($_[0]->value(0));
263 0           $str =~ s/([\$\@\%])/\\$1/g;
264 0           return quote $str;
265             }
266             }
267              
268             {
269             package Anarres::Mud::Driver::Compiler::Node::Integer;
270 0     0     sub generate { $_[0]->value(0) }
271             }
272              
273             {
274             package Anarres::Mud::Driver::Compiler::Node::Array;
275             sub generate {
276 0     0     my ($self, $indent, @rest) = @_;
277 0           $indent++;
278              
279 0           my @vals = map { $_->generate($indent, @rest) } $self->values;
  0            
280              
281 0 0         return "[ ]" unless @vals;
282              
283 0           $indent--;
284 0           my $isep = "\n" . ("\t" x $indent);
285 0           my $sep = "," . $isep . "\t";
286 0           return "[" . $isep . "\t" . join($sep, @vals) . $isep . "]";
287             }
288             }
289              
290             {
291             package Anarres::Mud::Driver::Compiler::Node::Mapping;
292             sub generate {
293 0     0     my ($self, $indent, @rest) = @_;
294 0           $indent++;
295              
296 0           my @vals = map { $_->generate($indent, @rest) } $self->values;
  0            
297 0 0         return "{ }" unless @vals;
298              
299 0           my @out = ();
300 0           while (my @tmp = splice(@vals, 0, 2)) {
301 0           push(@out, $tmp[0] . "\t=> " . $tmp[1] . ",");
302             }
303              
304 0           $indent--;
305 0           my $isep = "\n" . ("\t" x $indent);
306 0           my $sep = $isep . "\t";
307 0           return "{$isep\t" . join($sep, @out) . "$isep}";
308             }
309             }
310              
311             {
312             package Anarres::Mud::Driver::Compiler::Node::Closure;
313             # XXX This needs to store the owner object so we can emulate the
314             # LPC behaviour of function_owner. Something like [ $self, sub {} ]
315             sub generate {
316 0     0     my $self = shift;
317             # return "sub { " . $self->value(0)->generate(@_) . " }";
318 0           return '$self->{Closures}->[' . $self->value(1) . ']';
319             }
320             }
321              
322             {
323             package Anarres::Mud::Driver::Compiler::Node::VarLocal;
324             sub generate {
325 0     0     return '$_L_' . $_[0]->value(0);
326             }
327             }
328              
329             {
330             package Anarres::Mud::Driver::Compiler::Node::VarGlobal;
331             sub generate {
332 0     0     my $self = shift;
333 0           my $name = $self->value(0);
334 0           return '$self->{Variables}->{_G_' . $name . '}';
335             }
336             }
337              
338             {
339             package Anarres::Mud::Driver::Compiler::Node::VarStatic;
340             sub generate {
341 0     0     return '$_S_' . $_[0]->value(0);
342             }
343             }
344              
345             {
346             package Anarres::Mud::Driver::Compiler::Node::Parameter;
347 0     0     sub generate { '$_[' . $_[0]->value(0) . ']' }
348             }
349              
350             {
351             package Anarres::Mud::Driver::Compiler::Node::Funcall;
352             sub generate {
353 0     0     my $self = shift;
354 0           my @args = $self->values;
355 0           my $method = shift @args;
356 0           @args = map { $_->generate(@_) } @args;
  0            
357 0           return $method->generate_call(@args);
358             }
359             }
360              
361             {
362             package Anarres::Mud::Driver::Compiler::Node::CallOther;
363             sub generate {
364 0     0     my $self = shift;
365 0           my @values = $self->values;
366 0           my $exp = shift @values;
367 0           my $name = shift @values;
368 0           @values = map { $_->generate(@_) } @values;
  0            
369 0           return '(' . $exp->generate(@_) . ')->' . $name . '(' .
370             join(", ", @values) . ')';
371 0           q[
372             do {
373             my ($exp, @vals) = (....);
374             ref($exp) && ! $exp->{Flags}->{Destructed}
375             or die "Call into destructed or nonobject.";
376             $exp->func(@vals);
377             ] if 0;
378             }
379             }
380              
381             {
382             package Anarres::Mud::Driver::Compiler::Node::StrIndex;
383             # XXX Use the core subchar efun
384             }
385              
386             {
387             package Anarres::Mud::Driver::Compiler::Node::StrRange;
388             # XXX Use the core substr efun
389              
390             {
391             *generate_cst_ll = __PACKAGE__->gensub('StrRangeLL (constant)',
392             $OPCODETABLE{'StrRangeCstLL'});
393             }
394             # Don't do this!
395             sub generate_cst ($) {
396 3     3   35 no warnings qw(redefine);
  3         8  
  3         303  
397 0 0   0     return undef unless $]; # Defeat inlining
398 0           my $self = shift;
399 0           *generate_cst = $self->gensub('StrRange (constant LL)',
400             $OPCODETABLE{'StrRangeCstLL'});
401 0           return $self->generate_cst(@_);
402             }
403             sub generate_var ($) {
404 3     3   13 no warnings qw(redefine);
  3         7  
  3         586  
405 0 0   0     return undef unless $]; # Defeat inlining
406 0           my $self = shift;
407 0           *generate_var = $self->gensub('StrRange (variable)',
408             $OPCODETABLE{'StrRangeVarLL'});
409 0           return $self->generate_var(@_);
410             }
411             # XXX We need to check for lvalues around here. :-(
412             sub generate {
413 0     0     my $self = shift;
414 0           my $val = $self->value(1);
415             # Variables are unchanged across this operation.
416             # What we really mean here is, "Is it pure?"
417             # But that would not necessarily amount to an optimisation.
418             # A better question might be, "Is it elementary?"
419             # (VarLocal or VarGlobal)
420 0 0 0       if (ref($val) =~ /::Var(Local|Global|Static)$/ || ($val->flags)&F_CONST) {
421 0           return $self->generate_cst(@_);
422             }
423             else {
424 0           return $self->generate_var(@_);
425             }
426             }
427             }
428              
429             {
430             package Anarres::Mud::Driver::Compiler::Node::ArrRange;
431             sub generate_ll ($) {
432 3     3   17 no warnings qw(redefine);
  3         7  
  3         340  
433 0 0   0     return undef unless $]; # Defeat inlining
434 0           my $self = shift;
435 0           *generate_var = $self->gensub('ArrRange (LL)',
436             $OPCODETABLE{'ArrRangeLL'});
437 0           return $self->generate_var(@_);
438             }
439             sub generate {
440 0     0     my $self = shift;
441 0           return $self->generate_ll(@_);
442             }
443             }
444              
445             {
446             package Anarres::Mud::Driver::Compiler::Node::Scanf;
447 3     3   2516 use String::Scanf;
  3         5183  
  3         4667  
448             *invoke = \&String::Scanf::sscanf; # For consistency.
449             sub generate {
450 0     0     my $self = shift;
451 0           my ($exp, $fmt, @values) = $self->values;
452 0           @values = map { $_->generate(@_) } @values;
  0            
453 0           return __PACKAGE__ . '::invoke((' . $exp->generate(@_) . '), ('.
454             $fmt->generate(@_) . '), (' .
455             join('), (', @values) . '))';
456             }
457             }
458              
459             {
460             package Anarres::Mud::Driver::Compiler::Node::ArrOr;
461             # XXX Generate this inline like ArrSub.
462             sub invoke {
463 0     0     my @left = @{ $_[0] };
  0            
464 0           my %table = map { $_ => 1 } @left;
  0            
465 0           foreach (@{ $_[1] }) {
  0            
466 0 0         push(@left, $_) unless $table{$_}++; # Is the ++ right?
467             }
468             # () | (1, 1) = (1) or (1, 1) ?
469 0           return \@left;
470             }
471             sub generate {
472 0     0     my $self = shift;
473 0           return __PACKAGE__ . '::invoke(('.
474             $self->value(0)->generate(@_) . '), (' .
475             $self->value(1)->generate(@_) . '))';
476             }
477             }
478              
479             {
480             package Anarres::Mud::Driver::Compiler::Node::ArrAnd;
481             # XXX Generate this inline like ArrSub.
482             # sub infer { $_[1]->arrayp ? $_[0] : undef }
483             sub invoke {
484 0     0     my @out = ();
485 0           my %table = map { $_ => 1 } @{ $_[1] };
  0            
  0            
486 0           foreach (@{ $_[0] }) {
  0            
487 0 0         push(@out, $_) if $table{$_};
488             }
489 0           return \@out;
490             }
491             sub generate {
492 0     0     my $self = shift;
493 0           return 'Anarres::Mud::Driver::Compiler::Node::ArrIsect::invoke('.
494             $self->value(0)->generate(@_) . ', ' .
495             $self->value(1)->generate(@_) . ')';
496             }
497             }
498              
499             {
500             package Anarres::Mud::Driver::Compiler::Node::Block;
501             sub generate {
502 0     0     my ($self, $indent, @rest) = @_;
503 0           $indent++;
504              
505 0           my @args = map { $_->name } @{ $self->value(0) };
  0            
  0            
506 0           my @vals = map { $_->generate($indent, @rest) }
  0            
507 0           @{ $self->value(1) };
508             # We can't even return a comment in here in case we get
509             # do { # comment } while (undef) in various places.
510             # We have to have _something_ here in case we compile
511             # if (x) { } and we promote_to_block the second arg.
512 0 0         return '{ undef; }' unless @vals;
513              
514 0           $indent--;
515 0           my $isep = "\n" . ("\t" x $indent);
516 0           my $sep = $isep . "\t";
517 0 0         my $args = @args
518             ? 'my ($_L_' . join(', $_L_', @args) . ');' . $sep
519             : ''; # '# no locals in block'
520 0           return '{' . $sep . $args . join($sep, @vals) . $isep . "}";
521             }
522             }
523              
524             {
525             package Anarres::Mud::Driver::Compiler::Node::StmtSwitch;
526             sub generate {
527 0     0     my $self = shift;
528 0           my $indent = shift;
529              
530 0           my $isep = "\n" . ("\t" x $indent);
531 0           my $sep = $isep . "\t";
532              
533 0           $indent++;
534              
535 0           my ($exp, $block) = $self->values;
536 0           my $dump = $exp->dump;
537 0           $dump =~ s/\s+/ /g;
538 0           my $labels = $self->value(3);
539             # default label or end of switch
540 0   0       my $default = $self->value(4) || $self->value(2);
541              
542             # Put this n program header?
543 0           my @hashdata =
544 0           map { $sep . "\t\t" .
545             $labels->{$_}->generate($indent, @_) .
546             "\t=> '" . $_ . "'," }
547 0           keys %{ $labels };
548 0           my $hashdata = join('', @hashdata);
549              
550 0           return '{' .
551             $sep . '# ([v] switch ' . $dump . ')' .
552             $sep . 'my %__LABELS = (' . $hashdata . $sep . "\t\t" . ');'
553             .
554             # $sep . '# ' . join(", ", keys %{ $labels }) .
555             $sep . 'my $__a = ' . $exp->generate($indent, @_) . ';' .
556             $sep . 'exists $__LABELS{$__a} ' .
557             '? goto $__LABELS{$__a} ' .
558             ': goto ' . $default . ';' .
559             $sep . $block->generate($indent, @_) .
560             $sep . $self->value(2) . ':' .
561             $isep . '}';
562             }
563             }
564              
565             {
566             package Anarres::Mud::Driver::Compiler::Node::StmtCase;
567             sub generate {
568 0     0     my $self = shift;
569 0           my $indent = shift;
570 0           my $sep = "\n" . ("\t" x $indent);
571 0           my $dump = $self->dump;
572 0           $dump =~ s/\s+/ /g;
573             return
574 0           '# ' . $dump . $sep .
575             # This goto makes sure that a preceding label has at
576             # least one statement.
577             # 'goto ' . $self->value(2) . '; ' . $self->value(2) . ':';
578             '; ' . $self->value(2) . ':'; # Will this do?
579             }
580             }
581              
582             {
583             package Anarres::Mud::Driver::Compiler::Node::StmtDefault;
584             sub generate {
585 0     0     my $self = shift;
586 0           return $self->value(0) . ': # default';
587             }
588             }
589              
590             {
591             package Anarres::Mud::Driver::Compiler::Node::StmtBreak;
592             sub generate {
593 0     0     my $self = shift;
594 0           my $val = $self->value(0);
595 0 0         return 'next; # break' unless $val;
596 0           return 'goto ' . $val . '; # break';
597             }
598             }
599              
600             {
601             package Anarres::Mud::Driver::Compiler::Node::StmtRlimits;
602             sub generate {
603 0     0     my $self = shift;
604 0           return $self->value(3)->generate(@_) . ';';
605             }
606             }
607              
608             {
609             package Anarres::Mud::Driver::Compiler::Node::StmtIf;
610             sub generate {
611 0     0     my ($self, $indent, @args) = @_;
612 0           my $sep = "\t" x $indent;
613 0           my $out =
614             "if (" .
615             $self->value(0)->generate($indent + 2, @args) . ") " .
616             $self->value(1)->generate($indent, @args);
617 0           my $else = $self->value(2);
618 0 0         if ($else) {
619 0 0         if (ref($else) =~ /::StmtIf$/) {
620             # Get an 'elsif'
621 0           $out .= "\n" . $sep . "els" .
622             $else->generate($indent, @args);
623             }
624             else {
625 0           $out .=
626             "\n" . $sep . "else " .
627             $else->generate($indent, @args);
628             }
629             }
630 0           return $out;
631             }
632             # XXX Hack!
633             *Anarres::Mud::Driver::Compiler::Node::StmtIfElse::generate =
634             \&Anarres::Mud::Driver::Compiler::Node::StmtIf::generate;
635             }
636              
637             if (1) {
638             my $package = __PACKAGE__;
639             $package =~ s/::Generate$/::Node/;
640 3     3   30 no strict qw(refs);
  3         7  
  3         659  
641             my @missing;
642             foreach (@NODETYPES) {
643             next if defined $OPCODETABLE{$_};
644             next if defined &{ "$package\::$_\::generate" };
645             push(@missing, $_);
646             }
647             print "No generate in @missing\n" if @missing;
648             }
649              
650             1;