File Coverage

blib/lib/Anarres/Mud/Driver/Compiler/Check.pm
Criterion Covered Total %
statement 51 543 9.3
branch 1 246 0.4
condition 1 12 8.3
subroutine 16 74 21.6
pod 0 17 0.0
total 69 892 7.7


line stmt bran cond sub pod time code
1             package Anarres::Mud::Driver::Compiler::Check;
2              
3 3     3   1724 use strict;
  3         6  
  3         119  
4 3         339 use vars qw(@ISA @EXPORT_OK @STACK $DEBUG
5 3     3   15 %OPTYPETABLE %OPTYPES %OPCHOICES);
  3         7  
6 3     3   15 use Carp qw(:DEFAULT cluck);
  3         7  
  3         398  
7 3     3   16 use Data::Dumper;
  3         5  
  3         124  
8 3     3   16 use List::Util qw(first);
  3         6  
  3         299  
9 3     3   16 use Anarres::Mud::Driver::Compiler::Type qw(:all);
  3         5  
  3         806  
10 3     3   16 use Anarres::Mud::Driver::Compiler::Node qw(:all);
  3         6  
  3         9273  
11              
12             # This has turned into a rather long, complex and involved Perl file.
13              
14             # Error messages starting with [D] are duplicating work done elsewhere
15             # and are candidates for removal.
16              
17             push(@Anarres::Mud::Driver::Compiler::Node::ISA, __PACKAGE__);
18              
19             sub DBG_TC_NAME () { 1 }
20             sub DBG_TC_PROMOTE () { 2 }
21             sub DBG_TC_CONVERT () { 4 }
22              
23             $DEBUG = 0;;
24             $DEBUG |= DBG_TC_NAME if 0;
25             $DEBUG |= DBG_TC_PROMOTE if 0;
26             $DEBUG |= DBG_TC_CONVERT if 0;
27              
28             @STACK = ();
29              
30             sub debug_tc {
31 0     0 0 0 my ($self, $class, @args) = @_;
32 0 0       0 return undef unless $DEBUG & $class;
33 0         0 my $msg = join(": ", @args);
34 0         0 print STDERR "DebugTC: $msg\n";
35             }
36              
37             # Called at the beginning of any typecheck call
38             sub tc_start {
39 0     0 0 0 my ($self, @args) = @_;
40 0         0 push(@STACK, $self);
41 0         0 $self->debug_tc(DBG_TC_NAME, "Checking " . $self->opcode, @args);
42             }
43              
44             # Called at the end of any typecheck call, possibly by tc_fail().
45             sub tc_end {
46 0     0 0 0 my ($self, $type, @args) = @_;
47 0 0       0 $self->settype($type) if $type;
48 0         0 $self->debug_tc(DBG_TC_NAME, "Finished " . $self->opcode, @args);
49 0         0 pop(@STACK);
50 0         0 return 1; # Make it return a success.
51             }
52              
53             # This is a utility method. Calling it is mandatory
54             # in the case of failure.
55             sub tc_fail {
56 0     0 0 0 my ($self, $type, @args) = @_;
57 0 0       0 $type = T_FAILED unless $type;
58 0         0 $self->tc_end($type, @args);
59 0         0 return undef; # Make it return a failure.
60             }
61              
62              
63              
64              
65 48     48 0 228 sub LV ($) { return [ $_[0], F_LVALUE ] }
66              
67             # Opcodes which are choice targets and provide a custom convert
68             # are marked up as 'NOCHECK'.
69              
70             %OPTYPES = (
71             StmtNull => [ T_VOID ],
72             ExpComma => 'CODE',
73              
74             (map { $_ => 'NOCHECK' } qw(
75             IntAssert StrAssert ArrAssert MapAssert ClsAssert ObjAssert
76             ToString
77             )),
78              
79             # It's faster to give these two custom code as well.
80             # Nil => [ T_NIL ],
81             # String => [ T_STRING ],
82             (map { $_ => 'CODE' } qw(
83             Nil String Integer Array Mapping Closure Variable Parameter
84             Funcall CallOther
85             )),
86             (map { $_ => 'NOCHECK' } qw(
87             VarStatic VarGlobal VarLocal
88             )),
89              
90             Unot => [ T_UNKNOWN, T_BOOL ],
91             Tilde => [ T_INTEGER, T_INTEGER ],
92             Plus => [ T_INTEGER, T_INTEGER ],
93             Minus => [ T_INTEGER, T_INTEGER ],
94              
95             Postinc => [ LV(T_INTEGER), T_INTEGER ],
96             Postdec => [ LV(T_INTEGER), T_INTEGER ],
97             Preinc => [ LV(T_INTEGER), T_INTEGER ],
98             Predec => [ LV(T_INTEGER), T_INTEGER ],
99             (map { $_ => 'CHOOSE' } qw(
100             Eq Ne Lt Gt Le Ge
101              
102             Add Sub Mul Div Mod
103             Or And Xor
104             Lsh Rsh
105              
106             AddEq SubEq DivEq MulEq ModEq
107             AndEq OrEq XorEq
108             LshEq RshEq
109             )),
110              
111             (map { $_ => 'CODE' } qw(
112             LogOr LogAnd
113             LogOrEq LogAndEq
114             )),
115              
116             IntEq => [ T_INTEGER, T_INTEGER, T_BOOL ],
117             IntNe => [ T_INTEGER, T_INTEGER, T_BOOL ],
118             IntGe => [ T_INTEGER, T_INTEGER, T_BOOL ],
119             IntLe => [ T_INTEGER, T_INTEGER, T_BOOL ],
120             IntGt => [ T_INTEGER, T_INTEGER, T_BOOL ],
121             IntLt => [ T_INTEGER, T_INTEGER, T_BOOL ],
122              
123             IntAdd => [ T_INTEGER, T_INTEGER, T_INTEGER ],
124             IntSub => [ T_INTEGER, T_INTEGER, T_INTEGER ],
125             IntMul => [ T_INTEGER, T_INTEGER, T_INTEGER ],
126             IntDiv => [ T_INTEGER, T_INTEGER, T_INTEGER ],
127             IntMod => [ T_INTEGER, T_INTEGER, T_INTEGER ],
128              
129             IntAnd => [ T_INTEGER, T_INTEGER, T_INTEGER ],
130             IntOr => [ T_INTEGER, T_INTEGER, T_INTEGER ],
131             IntXor => [ T_INTEGER, T_INTEGER, T_INTEGER ],
132              
133             IntLsh => [ T_INTEGER, T_INTEGER, T_INTEGER ],
134             IntRsh => [ T_INTEGER, T_INTEGER, T_INTEGER ],
135              
136             IntAddEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
137             IntSubEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
138             IntMulEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
139             IntDivEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
140             IntModEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
141              
142             IntAndEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
143             IntOrEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
144             IntXorEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
145              
146             IntLshEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
147             IntRshEq => [ LV(T_INTEGER), T_INTEGER, T_INTEGER ],
148              
149              
150             StrEq => [ T_STRING, T_STRING, T_BOOL ],
151             StrNe => [ T_STRING, T_STRING, T_BOOL ],
152             StrGe => [ T_STRING, T_STRING, T_BOOL ],
153             StrLe => [ T_STRING, T_STRING, T_BOOL ],
154             StrGt => [ T_STRING, T_STRING, T_BOOL ],
155             StrLt => [ T_STRING, T_STRING, T_BOOL ],
156              
157             StrAdd => [ T_STRING, T_STRING, T_STRING ],
158             StrMul => [ T_STRING, T_STRING, T_STRING ],
159              
160             StrAddEq => [ LV(T_STRING), T_INTEGER, T_INTEGER ],
161             StrMulEq => [ LV(T_STRING), T_INTEGER, T_INTEGER ],
162              
163             ArrEq => [ T_UNKNOWN->array, T_UNKNOWN->array,T_BOOL ],
164             ArrNe => [ T_UNKNOWN->array, T_UNKNOWN->array,T_BOOL ],
165             # ArrAdd and ArrSub are the target of the Add and Sub choices.
166             (map { $_ => 'NOCHECK' } qw(
167             ArrAdd ArrSub
168             ArrOr ArrAnd
169             )),
170              
171             MapEq => [ T_UNKNOWN->mapping, T_UNKNOWN->mapping, T_BOOL ],
172             MapNe => [ T_UNKNOWN->mapping, T_UNKNOWN->mapping, T_BOOL ],
173             # These are choice targets.
174             (map { $_ => 'NOCHECK' } qw(
175             MapAdd MapSub
176             )),
177              
178             ObjEq => [ T_OBJECT, T_OBJECT, T_BOOL ],
179             ObjNe => [ T_OBJECT, T_OBJECT, T_BOOL ],
180              
181             # These actually have custom choose routines.
182             (map { $_ => 'CHOOSE' } qw(
183             Index Range
184             )),
185              
186             StrIndex => [ T_STRING, T_INTEGER, undef, T_INTEGER ],
187             StrRange => [ T_STRING, T_INTEGER, T_INTEGER, undef, undef,
188             T_INTEGER ],
189             # These are choice targets with nonstatic types
190             (map { $_ => 'NOCHECK' } qw(
191             ArrIndex ArrRange
192             MapIndex
193             )),
194             # These have nonstatic types
195             (map { $_ => 'CODE' } qw(
196             Member New
197             )),
198              
199             Catch => [ T_UNKNOWN, T_STRING ],
200              
201             Assign => 'CODE', # Output type is input type
202              
203             ExpCond => 'CODE', # Output type is unification of input
204              
205             Block => 'CODE', # Iterate over statements
206              
207             StmtExp => [ T_UNKNOWN, T_VOID ],
208             StmtRlimits => [ T_INTEGER, T_INTEGER, 'BLOCK', T_VOID ],
209             StmtTry => 'CODE',
210             StmtCatch => [ 'BLOCK', T_VOID ],
211              
212             # XXX These have to set up break and continue targets.
213             StmtDo => [ T_BOOL, 'BLOCK', T_VOID ],
214             StmtWhile => [ T_BOOL, 'BLOCK', T_VOID ],
215             StmtFor => [ T_VOID, T_BOOL, T_VOID, 'BLOCK', T_VOID ],
216             (map { $_ => 'CODE' } qw(
217             StmtForeach StmtForeachArr StmtForeachMap
218             )),
219              
220             # StmtBreak also needs code to get the label.
221             # Most of the flow control statements probably need code.
222             StmtSwitch => 'CODE', # Open a new switch context
223             StmtCase => 'CODE', # Generate a label
224             StmtDefault => 'CODE', # Sort out the labels
225             StmtIf => 'CODE', # Handle the 'else' clause!
226             StmtBreak => 'CODE', # Get the break target
227             StmtContinue=> 'CODE', # Get the continue target
228             StmtReturn => 'CODE', # Output type must match function
229              
230             Sscanf => 'CODE', # Urgh!
231             );
232              
233             # This looks like a fast way of generating the choice table for
234             # promotable operators, but does depend a little on the naming
235             # of opcodes! If there are any special cases, they need to be put
236             # into %OPCHOICES as literals. I'm going to get lynched for this.
237             {
238             %OPCHOICES = ();
239 3     3   20 no strict qw(refs);
  3         7  
  3         4857  
240             my $package = __PACKAGE__;
241             $package =~ s/[^:]+$/Node/;
242             foreach my $op (keys %OPTYPES) {
243             next unless $OPTYPES{$op} eq 'CHOOSE';
244             foreach my $tp (qw(Int Str Obj Arr Map)) {
245             push(@{ $OPCHOICES{$op} }, "$tp$op") if $OPTYPES{"$tp$op"};
246             }
247             }
248             }
249             # We can't do this because we then don't pass the new opcode type
250             # in the case that we're calling the superclass method! Furthermore,
251             # the subclass method we actually try to call won't exist.
252             # my $sub = \&{ "$package\::$tp$op::convert" }
253             # or die "No 'convert' in package $package\::$tp$op";
254              
255              
256             # A lot of superclass methods. These are found in ::Check via @ISA.
257              
258 0     0 0 0 sub lvaluep { undef; }
259 0     0 0 0 sub constp { undef; }
260              
261             sub assert { # This sucks somewhat
262 0     0 0 0 my ($self, $type) = @_;
263 0 0       0 if (!$self->type->equals(T_UNKNOWN)) { # DEBUGGING
264 0         0 confess "Asserting something of known type.";
265             }
266 0         0 print "Asserting " . $self->opcode . " into " . ${$type} . "\n";
  0         0  
267 0 0       0 return new Anarres::Mud::Driver::Compiler::Node::IntAssert($self)
268             if $type->equals(T_INTEGER);
269 0 0       0 return new Anarres::Mud::Driver::Compiler::Node::StrAssert($self)
270             if $type->equals(T_STRING);
271 0 0       0 return new Anarres::Mud::Driver::Compiler::Node::ArrAssert($self)
272             if $type->is_array;
273 0 0       0 return new Anarres::Mud::Driver::Compiler::Node::MapAssert($self)
274             if $type->is_mapping;
275 0 0       0 return new Anarres::Mud::Driver::Compiler::Node::ClsAssert($self)
276             if $type->equals(T_CLOSURE);
277 0 0       0 return new Anarres::Mud::Driver::Compiler::Node::ObjAssert($self)
278             if $type->equals(T_OBJECT);
279 0         0 confess "Cannot assert node into type " . $$type . "!\n";
280 0         0 return undef;
281             }
282              
283             sub promote_to_block {
284 0     0 0 0 my ($self, $stmt) = @_;
285              
286 0 0       0 return $stmt if ref($stmt) =~ /::Block$/;
287 0 0       0 confess "Can only promote statements into blocks, not " .
288             $stmt->opcode
289             unless ref($stmt) =~ /::Stmt[^:]+$/;
290              
291             # It's a statement. This code is partially duplicated below.
292 0         0 return new Anarres::Mud::Driver::Compiler::Node::Block(
293             [], # locals
294             [ $stmt ]);
295             }
296              
297             sub idx_promote_to_block {
298 0     0 0 0 my ($self, $index) = @_;
299 0         0 my $stmt = $self->value($index);
300 0         0 my $block = $self->promote_to_block($stmt);
301 0         0 $self->setvalue($index, $block);
302 0         0 return $block;
303             }
304              
305             # There is a special case of this in Integer.
306             sub promote {
307 0     0 0 0 my ($self, $newtype) = @_;
308 0         0 my $type = $self->type;
309             # XXX Checking for T_UNKNOWN is wrong here. I need to check
310             # whether the old type is 'weaker' than the new type.
311 0 0       0 confess "XXX No type in " . $self->dump unless $type;
312 0 0       0 return $self if $type->equals($newtype);
313 0         0 $self->debug_tc(DBG_TC_PROMOTE, "Promoting ([" . $type->dump . "] ".
314             $self->opcode . ") into " . $newtype->dump);
315              
316             # Anything can become 'unknown' - this allows weakening
317 0 0       0 return $self if $type->compatible($newtype);
318              
319             # This should really be done by 'compatible'?
320 0 0       0 return $self if $newtype->equals(T_BOOL);
321              
322             # The Assert nodes are broken for some reason?
323             # return $self->assert($newtype) if $type->equals(T_UNKNOWN);
324 0 0       0 return $self if $type->equals(T_UNKNOWN); # Should assert
325              
326 0 0 0     0 return $self
327             if $type->equals(T_INTEGER) && $newtype->equals(T_STRING);
328             # return $type->promote($self, $newtype);
329 0         0 return undef;
330             }
331              
332             # This might return an undef in the error list in the case that an
333             # error occurred which has already been reported.
334             sub convert {
335 0     0 0 0 my ($self, $program, @rest) = @_;
336              
337 0         0 my $opcode = $self->opcode;
338              
339 0         0 $self->debug_tc(DBG_TC_CONVERT, "Convert " . $self->opcode .
340             " to " . $opcode);
341              
342 0 0       0 unless (ref $OPTYPES{$opcode}) {
343 0 0       0 confess "XXX OPTYPES for $opcode is $OPTYPES{$opcode}"
344             if $OPTYPES{$opcode};
345 0         0 confess "XXX No OPTYPES for $opcode!";
346             }
347              
348 0         0 my @values = $self->values;
349 0         0 my @template = @{ $OPTYPES{$opcode} };
  0         0  
350 0         0 my $rettype = pop(@template);
351              
352 0 0       0 unless (@values == @template) {
353             # XXX This is for self-debugging.
354 0         0 print STDERR "I have " . scalar(@values) . " values\n";
355 0         0 print STDERR "I have " . scalar(@template) . " template\n";
356 0         0 die "Child count mismatch in $opcode";
357             }
358              
359             # We push undef into @errors to indicate that an error occurred
360             # but should have been reported already at a lower level.
361              
362 0         0 my $i = 0;
363 0         0 my @tvals = ();
364 0         0 my @errors = ();
365 0         0 foreach my $type (@template) {
366 0         0 my $val = $values[$i];
367 0         0 my ($tval, @assertions);
368              
369             # XXX I should promote unknown to anything, not
370             # assert directly in convert.
371              
372 0 0       0 if (ref($type) eq 'ARRAY') {
373 0         0 @assertions = @$type;
374 0         0 $type = shift @assertions;
375             }
376              
377 0 0       0 if (!defined $type) {
    0          
378 0         0 $tval = $val;
379             }
380             elsif ($type eq 'BLOCK') {
381 0         0 $tval = $self->promote_to_block($val);
382 0 0       0 $tval->check($program, @rest)
383             or push(@errors, undef);
384             }
385             else {
386 0 0       0 if (!$val->check($program, @rest)) {
    0          
387 0         0 push(@errors, undef);
388             }
389             elsif (!($tval = $val->promote($type))) {
390 0         0 push(@errors, "Cannot promote " . $val->opcode .
391             " from " . $val->type->name .
392             " to " . $type->name .
393             " for argument $i of " . $self->opcode);
394             }
395             }
396              
397             # return undef unless $tval;
398              
399             # XXX Perform assertions.
400 0         0 foreach (@assertions) {
401 0 0       0 if ($_ == F_LVALUE) {
402 0 0       0 unless ($tval->lvaluep) {
403 0         0 push(@errors, $val->opcode . " is not an lvalue in "
404             . $self->opcode);
405             }
406             }
407             else {
408 0         0 die "Unknown assertion $_!";
409             }
410             }
411              
412 0         0 push(@tvals, $tval);
413             }
414             continue {
415 0         0 $i++;
416             }
417              
418 0 0       0 return @errors if @errors;
419              
420             # Hack the node gratuitously. Should I use 2+$#tvals?
421 0         0 splice(@$self, 2, $#$self, @tvals);
422 0         0 $self->settype($rettype);
423              
424             # We might also have a package change.
425 0         0 my $package = ref($self);
426 0         0 $package =~ s/::[^:]*$/::$opcode/;
427 0         0 bless $self, $package;
428              
429 0         0 return ();
430             }
431              
432             sub choose {
433 0     0 0 0 my ($self, $program, @rest) = @_;
434              
435 0         0 $self->tc_start;
436              
437 0         0 my $opcode = $self->opcode;
438              
439             # If everything follows the pattern, or at least a large
440             # amount of it does, then it would be worth iterating over
441             # Int, Str, Arr, Map here instead of having OPCHOICES at all.
442             # That might smell a bit more like black magic though.
443             # Alternatively, I could embed the choices into the OPTYPES
444             # table, but that might involve more magic stash hacking
445             # to optimise.
446 0         0 my @failures;
447 0         0 foreach (@{ $OPCHOICES{$opcode} }) {
  0         0  
448 0         0 $self->setopcode($_);
449 0         0 my @errors = $self->convert($program, @rest);
450 0 0       0 return $self->tc_end unless @errors;
451 0         0 push(@failures, \@errors);
452             }
453 0         0 $self->setopcode($opcode); # Might as well restore.
454              
455             # Make @errors contain only the error messages from the attempted
456             # conversions which produced the fewest errors.
457 0         0 my @counts;
458 0         0 foreach (@failures) {
459 0         0 push(@{ $counts[@$_] }, $_);
  0         0  
460             }
461 0     0   0 my $minimum = first { defined $_ } @counts;
  0         0  
462 0         0 my @errors = map { @$_ } @$minimum;
  0         0  
463              
464 0         0 $program->error("Cannot convert $opcode into any available choice: "
465             . Dumper(\@errors));
466              
467 0         0 return $self->tc_fail;
468             }
469              
470             # Actually, this is kind of like an optimised 'choose'
471             sub convert_or_fail {
472 0     0 0 0 my ($self, $program, @rest) = @_;
473 0         0 $self->tc_start;
474 0         0 my $opcode = $self->opcode;
475 0         0 my @errors = $self->convert($program, @rest);
476 0 0       0 return $self->tc_end unless @errors;
477             # Remove errors which should have been reported already
478 0         0 @errors = grep { defined $_ } @errors;
  0         0  
479 0 0       0 $program->error("Failed to typecheck $opcode:\n\t" .
480             join("\n\t", @errors))
481             if @errors;
482 0         0 return $self->tc_fail(T_FAILED);
483             }
484              
485             # This doesn't call tc_start/tc_end because it modifies the stash
486             # in the class it's called in to point to another function. The
487             # superclass versions of those new functions must themselves call
488             # tc_start/tc_end.
489             sub check {
490 0     0 0 0 my ($self, $program, @rest) = @_;
491              
492 0 0       0 if ($self->type) {
493             # carp "Have already typechecked " . $self->opcode .
494             # " " . (0+$self);
495 0         0 return 1;
496             }
497              
498 0         0 my $opcode = $self->opcode;
499 0         0 my $subname = ref($self) . '::check';
500              
501             # We have to use can() here because some classes
502             # have custom choose/convert overrides.
503              
504 0 0       0 if (ref($OPTYPES{$opcode}) eq 'ARRAY') {
    0          
    0          
    0          
505 3     3   17 no strict qw(refs);
  3         7  
  3         176  
506 0         0 *{ $subname } = $self->can('convert_or_fail');
  0         0  
507 0         0 return $self->convert_or_fail($program, @rest);
508             }
509             elsif ($OPTYPES{$opcode} eq 'CHOOSE') {
510 3     3   13 no strict qw(refs);
  3         7  
  3         767  
511 0         0 *{ $subname } = $self->can('choose');
  0         0  
512 0         0 return $self->choose($program, @rest);
513             }
514             elsif ($OPTYPES{$opcode} eq 'NOCHECK') {
515 0         0 die "Cannot check NOCHECK opcode $opcode";
516             }
517             elsif ($OPTYPES{$opcode} eq 'CODE') {
518 0         0 die "Cannot auto-check CODE opcode $opcode";
519             }
520             else {
521 0         0 die "What is $OPTYPES{$opcode}?";
522             }
523              
524 0         0 die "How did I get to the end of the superclass check() method?";
525             }
526              
527             # This routine shouldn't be reporting. A failure should be reporting
528             # itself, with the parent from the typecheck stack.
529             sub check_children {
530 0     0 0 0 my ($self, $vals, @rest) = @_;
531              
532 0         0 my $ok = 1;
533              
534 0         0 foreach (@$vals) {
535 0 0       0 next unless $_; # We have some 'undef' statements.
536 0 0       0 $_->check(@rest)
537             or $ok = undef;
538             }
539              
540 0         0 return $ok;
541             }
542              
543             # A utility function called from various packages at boot time.
544             # It replaces code similar to the following in various packages.
545             # my $package = __PACKAGE__;
546             # $package =~ s/[^:]+$/Index/;
547             # no strict qw(refs);
548             # *lvaluep = \&{ "$package\::lvaluep" };
549              
550             sub steal {
551 21     21 0 36 my ($self, $victim, $subname) = @_;
552 21   33     79 my $target = ref($self) || $self;
553 21         39 my $source = $target;
554 21         103 $source =~ s/[^:]+$/$victim/;
555 3     3   16 no strict qw(refs);
  3         4  
  3         14635  
556 21 50       25 my $sub = \&{ "$source\::$subname" }
  21         112  
557             or confess "No such sub $subname in $source";
558 21         26 *{ "$target\::$subname" } = $sub;
  21         115  
559             }
560              
561             # Now the node-specific packages.
562              
563             {
564             package Anarres::Mud::Driver::Compiler::Node::Nil;
565 1     1   10 sub check { $_[0]->settype(T_NIL); $_[0]->setflag(F_CONST); 1; }
  1         9  
  1         4  
566             }
567              
568             {
569             package Anarres::Mud::Driver::Compiler::Node::String;
570 0     0     sub check {$_[0]->settype(T_STRING); $_[0]->setflag(F_CONST); 1;}
  0            
  0            
571             }
572              
573             {
574             package Anarres::Mud::Driver::Compiler::Node::Integer;
575             # This doesn't start/end since it can't fail.
576 0     0     sub check {$_[0]->settype(T_INTEGER); $_[0]->setflag(F_CONST); 1;}
  0            
  0            
577             sub promote {
578 0     0     my ($self, $newtype, @rest) = @_;
579              
580             # Yes, a special case.
581 0 0         if ($self->value(0) == 0) { # A valid nil
582 0 0         unless ($newtype->equals(T_INTEGER)) {
583 0           my $nil = new Anarres::Mud::Driver::Compiler::Node::Nil;
584 0           $nil->check;
585 0           return $nil;
586             }
587             }
588              
589 0           return $self->SUPER::promote($newtype, @rest);
590             }
591             }
592              
593             {
594             package Anarres::Mud::Driver::Compiler::Node::Array;
595             sub check {
596 0     0     my ($self, $program, @rest) = @_;
597              
598 0           $self->tc_start;
599              
600 0           my @values = $self->values;
601 0 0         $self->check_children(\@values, $program, @rest)
602             or return $self->tc_fail(T_ARRAY);
603              
604 0           my $flag = F_CONST;
605 0           my $type = T_NIL;
606 0           foreach (@values) {
607             # Search the types to find a good type.
608 0           $type = $_->type->unify($type);
609 0           $flag &= $_->flags;
610             }
611              
612 0           $self->settype($type->array);
613 0 0         $self->setflag($flag) if $flag;
614              
615 0           return $self->tc_end;
616             }
617             }
618              
619             {
620             package Anarres::Mud::Driver::Compiler::Node::Mapping;
621             sub check {
622 0     0     my ($self, $program, @rest) = @_;
623              
624 0           $self->tc_start;
625              
626 0           my @values = $self->values;
627 0 0         $self->check_children(\@values, $program, @rest)
628             or return $self->tc_fail(T_MAPPING);
629              
630 0           my $ret = 1;
631              
632 0           my $flag = F_CONST;
633 0           my $type = T_NIL;
634 0           my $idx = 0;
635 0           foreach (@values) {
636             # Search the types to find a good type.
637 0 0         if ($idx & 1) {
638 0           $type = $_->type->unify($type);
639             }
640             else {
641 0           my $key = $_->promote(T_STRING);
642 0 0         if ($key) {
643 0           $self->setvalue($idx, $key);
644             }
645             else {
646 0           $program->error("Map keys must be strings, not " .
647             $_->dump);
648 0           $ret = undef;
649             }
650             }
651              
652 0           $flag &= $_->flags;
653 0           $idx++;
654             }
655              
656 0           $self->settype($type->mapping);
657 0 0         $self->setflag($flag) if $flag;
658              
659 0 0         return $ret ? $self->tc_end : $self->tc_fail;
660             }
661             }
662              
663             {
664             package Anarres::Mud::Driver::Compiler::Node::Closure;
665             # XXX Write this.
666             sub check {
667 0     0     my ($self, $program, @rest) = @_;
668 0           $self->tc_start;
669 0           $self->setvalue(1, $program->closure($self));
670 0           $self->settype(T_CLOSURE);
671 0           return $self->tc_end;
672             }
673             }
674              
675             {
676             package Anarres::Mud::Driver::Compiler::Node::Variable;
677 0     0     sub lvaluep { 1; }
678             # Look up type
679             sub check {
680 0     0     my ($self, $program, @rest) = @_;
681 0           my $name = $self->value(0);
682 0           $self->tc_start($name);
683 0           my ($var, $class);
684 0 0         confess "XXX No program" unless $program;
685 0 0         if ($var = $program->local($name)) {
    0          
686 0           $class = 'Anarres::Mud::Driver::Compiler::Node::VarLocal';
687             }
688             elsif ($var = $program->global($name)) {
689 0           $class = 'Anarres::Mud::Driver::Compiler::Node::VarGlobal';
690             }
691             # elsif ($var = $program->static($name)) {
692             # $class ='Anarres::Mud::Driver::Compiler::Node::VarStatic';
693             # }
694             else {
695 0           $program->error("Variable $name not found");
696             # XXX Should we fake something up? We end up
697             # dying later if we leave a Variable in the tree.
698 0           return $self->tc_fail;
699             }
700 0           bless $self, $class;
701 0           $self->settype($var->type);
702 0           return $self->tc_end;
703             }
704             # XXX As an rvalue? Delegate to a basic type infer method.
705             # XXX If it's an rvalue then it must be initialised. Also for ++, --
706             }
707              
708             {
709             package Anarres::Mud::Driver::Compiler::Node::VarStatic;
710 0     0     sub lvaluep { 1; }
711             }
712              
713             {
714             package Anarres::Mud::Driver::Compiler::Node::VarGlobal;
715 0     0     sub lvaluep { 1; }
716             }
717              
718             {
719             package Anarres::Mud::Driver::Compiler::Node::VarLocal;
720 0     0     sub lvaluep { 1; }
721             }
722              
723             {
724             package Anarres::Mud::Driver::Compiler::Node::Parameter;
725 0     0     sub lvaluep { 1; }
726             # XXX We could look this up at the current point ...
727 0     0     sub check { $_[0]->settype(T_UNKNOWN); return 1; } # XXX Do this!
  0            
728             }
729              
730             {
731             package Anarres::Mud::Driver::Compiler::Node::Funcall;
732             # Look up return type, number of args
733             sub check {
734 0     0     my ($self, $program, @rest) = @_;
735              
736             # Changing the format of this node will require modifications
737             # to StmtIf optimisation.
738 0           my @values = $self->values;
739 0           my $method = shift @values;
740              
741 0           $self->tc_start('"' . $method->proto . '"');
742              
743 0           my @failed = ();
744 0           my $ctr = 0;
745 0           foreach (@values) {
746 0 0         $_->check($program, @rest) or push(@failed, $ctr);
747 0           $ctr++;
748             }
749 0 0         if (@failed) {
750 0           $program->error("Failed to typecheck arguments @failed to "
751             . $method->name);
752             # XXX Wrong! Use the method's type. This should be some
753             # sensible default in the case of overloads. If we don't
754             # have overloads then we can evaluate the method's type
755             # already. We don't need to check the child nodes yet.
756 0           return $self->tc_fail(T_UNKNOWN);
757             }
758              
759 0           unshift(@values, $method);
760             # XXX Revisit typecheck_call fairly soon. It must report errors.
761 0           my $type = $method->typecheck_call($program, \@values);
762 0 0         return $self->tc_fail unless $type;
763 0           $self->settype($type);
764 0           return $self->tc_end;
765             }
766             }
767              
768             {
769             package Anarres::Mud::Driver::Compiler::Node::CallOther;
770             # XXX Look up return type?
771             sub check {
772 0     0     my ($self, $program, @rest) = @_;
773 0           my ($exp, $name, @values) = $self->values;
774 0           $self->tc_start;
775 0           unshift(@values, $exp);
776 0 0         $self->check_children(\@values, $program, @rest)
777             or return $self->tc_fail;
778             # XXX What if the lhs is type string?
779 0           $self->settype(T_UNKNOWN);
780 0           return $self->tc_end;
781             }
782             }
783              
784             {
785             package Anarres::Mud::Driver::Compiler::Node::Index;
786             sub lvaluep { # XXX This should live in StrIndex or ArrIndex
787 0 0   0     return 1 if $_[0]->flags & F_LVALUE;
788 0 0         if ($_[0]->value(0)->lvaluep) {
789 0           $_[0]->setflag(F_LVALUE);
790 0           return 1;
791             }
792 0           return undef;
793             }
794             }
795              
796             {
797             package Anarres::Mud::Driver::Compiler::Node::StrIndex;
798             __PACKAGE__->steal("Index", "lvaluep");
799             }
800              
801             {
802             package Anarres::Mud::Driver::Compiler::Node::ArrIndex;
803             __PACKAGE__->steal("Index", "lvaluep");
804              
805             # This isn't a 'sub check' because it's the target of a choice,
806             # and therefore it can't issue errors because it's called
807             # speculatively by the chooser.
808             sub convert {
809 0     0     my ($self, $program, @rest) = @_;
810 0           my ($val, $idx) = $self->values;
811 0           my @errors = ();
812              
813 0 0         $val->check($program, @rest)
814             or push(@errors, "Failed to check value " . $val->opcode);
815 0 0         $idx->check($program, @rest)
816             or push(@errors, "Failed to check index " . $idx->opcode);
817 0 0         $val->type->is_array
818             or push(@errors, "Cannot perform array index on " .
819             $val->type->name);
820 0 0         $idx->type->equals(T_INTEGER)
821             or push(@errors, "Cannot index on array with " .
822             $idx->type->name);
823 0 0         return @errors if @errors;
824 0           $self->settype($val->type->dereference);
825 0           bless $self, __PACKAGE__;
826 0           return ();
827             }
828             }
829              
830             {
831             package Anarres::Mud::Driver::Compiler::Node::MapIndex;
832             __PACKAGE__->steal("Index", "lvaluep");
833              
834             sub convert {
835 0     0     my ($self, $program, @rest) = @_;
836 0           my ($val, $idx, $endp) = $self->values;
837 0           my @errors = ();
838              
839 0 0         $val->check($program, @rest)
840             or push(@errors, "Failed to check value " . $val->opcode);
841 0 0         $idx->check($program, @rest)
842             or push(@errors, "Failed to check index " . $idx->opcode);
843 0 0         $val->type->is_mapping
844             or push(@errors, "Cannot perform mapping dereference on " .
845             $val->type->name);
846             # XXX Make this use promotion properly.
847 0 0 0       $idx->type->equals(T_STRING)
848             ||
849             $idx->type->equals(T_INTEGER)
850             or push(@errors, "Cannot index on mapping with " .
851             $idx->type->name);
852 0 0         return @errors if @errors;
853 0 0         $endp
854             and $program->error("Cannot index from end of mapping");
855 0           $self->settype($val->type->dereference);
856 0           bless $self, __PACKAGE__;
857 0           return ();
858             }
859             }
860              
861             {
862             package Anarres::Mud::Driver::Compiler::Node::Member;
863             sub lvaluep {
864 0 0   0     if ($_[0]->value(0)->lvaluep) {
865 0           $_[0]->setflag(F_LVALUE);
866 0           return 1;
867             }
868 0           return undef;
869             }
870              
871             sub check {
872 0     0     my ($self, $program, @rest) = @_;
873 0           $self->tc_start;
874 0           my ($value, $field) = $self->values;
875 0 0         $value->check($program, @rest)
876             or return $self->tc_fail;
877 0           my $type = $value->type;
878 0 0         if (!($type->is_class)) {
879 0           $program->error("Cannot get member $field of type " .
880             $type->name);
881             # print STDERR "Failed fragment is " . $value->dump, "\n";
882 0           return $self->tc_fail;
883             }
884 0           elsif (0) { # XXX Does the field exist?
885             $program->error("No field called $field in class " .
886             $type->class);
887             return $self->tc_fail;
888             }
889 0           my $ftype = $program->class_field_type($type->class, $field);
890 0           $self->settype($ftype); # Might be T_FAILED
891 0           return $self->tc_end;
892             }
893             }
894              
895             {
896             package Anarres::Mud::Driver::Compiler::Node::New;
897             sub check {
898 0     0     my ($self, $program, $flags, @rest) = @_;
899 0           my $cname = $self->value(0);
900 0           $self->tc_start("class $cname");
901 0           my $type = $program->class_type($cname);
902 0           $self->settype($type); # Might be T_FAILED
903 0           return $self->tc_end;
904             }
905             }
906              
907             # 1. Promote things to blocks.
908             # 2. Check children
909             # 3. Check that things are lvalues.
910             # 4. Check that things are appropriate types.
911             # 5. Rebless the current node.
912             # 6. Set the type of the current node.
913             # 7. Return a success or failure.
914              
915             {
916             package Anarres::Mud::Driver::Compiler::Node::Sscanf;
917             # This should be $_[1], @{$_[2]}
918             sub check {
919 0     0     my ($self, $program, $flags, @rest) = @_;
920 0           my @values = $self->values;
921 0           $self->tc_start;
922 0 0         $self->check_children(\@values, $program, @rest)
923             or return $self->tc_fail(T_INTEGER);
924              
925 0           my $exp = shift @values;
926 0           my $fmt = shift @values;
927              
928 0           my $sexp = $exp->promote(T_STRING);
929 0 0         unless ($sexp) {
930 0           $program->error("Input for sscanf must be string, not " .
931 0           ${ $exp->type });
932 0           return $self->tc_fail(T_INTEGER);
933             }
934 0           $self->setvalue(0, $sexp);
935              
936 0           my $sfmt = $fmt->promote(T_STRING);
937 0 0         unless ($sfmt) {
938 0           $program->error("Format for sscanf must be string, not " .
939             $fmt->type->dump);
940 0           return $self->tc_fail(T_INTEGER);
941             }
942 0           $self->setvalue(1, $sfmt);
943              
944 0           $self->settype(T_INTEGER);
945 0           return $self->tc_end;
946             }
947             }
948              
949             {
950             package Anarres::Mud::Driver::Compiler::Node::Assign;
951             sub check {
952 0     0     my ($self, $program, @rest) = @_;
953 0           my ($lval, $exp) = $self->values;
954              
955 0           $self->tc_start;
956              
957 0 0         $self->check_children([ $lval, $exp ], $program, @rest)
958             or return $self->tc_fail($exp->type);
959 0 0         unless ($lval->lvaluep) {
960 0           $program->error("lvalue to assign is not an lvalue");
961 0           return $self->tc_fail($exp->type);
962             }
963              
964             # XXX Use "compatible"
965 0           my $rval = $exp->promote($lval->type);
966 0 0         unless ($rval) {
967 0           my $dump = $lval->dump;
968 0           $dump =~ s/\s+/ /g;
969 0           $program->error("Cannot assign type " .
970             $exp->type->name . " to lvalue " .
971             $dump ." of type ". $lval->type->name);
972             # Assign always takes the type of the lvalue.
973 0           return $self->tc_fail($lval->type);
974             }
975              
976             # Perhaps this ought to be the more specific of the two types.
977              
978 0           $self->setvalue(1, $rval);
979 0           $self->settype($rval->type); # More accurate than lval->type
980              
981 0           return $self->tc_end;
982             }
983             }
984              
985             {
986             package Anarres::Mud::Driver::Compiler::Node::LogAnd;
987             sub check {
988 0     0     my ($self, $program, @rest) = @_;
989 0           my ($lval, $rval) = $self->values;
990 0           $self->tc_start;
991 0           my $ret = 1;
992 0 0         $lval->check($program, @rest) or $ret = undef;
993 0 0         $rval->check($program, @rest) or $ret = undef;
994 0 0         return $self->tc_fail unless $ret;
995 0           $self->settype($lval->type->unify($rval->type));
996 0           return $self->tc_end;
997             }
998             }
999              
1000             {
1001             package Anarres::Mud::Driver::Compiler::Node::LogOr;
1002             __PACKAGE__->steal("LogAnd", "check");
1003             }
1004              
1005             {
1006             package Anarres::Mud::Driver::Compiler::Node::LogAndEq;
1007             sub check {
1008 0     0     my ($self, $program, @rest) = @_;
1009 0           my ($lval, $rval) = $self->values;
1010 0           $self->tc_start;
1011 0           my $ret = 1;
1012 0 0         $lval->check($program, @rest) or $ret = undef;
1013 0 0         $rval->check($program, @rest) or $ret = undef;
1014 0 0         return $self->tc_fail unless $ret;
1015 0 0         unless ($lval->lvaluep) {
1016 0           $program->error("Lvalue to logical assignment is not an lvalue.");
1017 0           return $self->tc_fail;
1018             }
1019 0           $self->settype($lval->type->unify($rval->type));
1020 0           return $self->tc_end;
1021             }
1022             }
1023              
1024             {
1025             package Anarres::Mud::Driver::Compiler::Node::LogOrEq;
1026             __PACKAGE__->steal("LogAndEq", "check");
1027             }
1028              
1029             {
1030             package Anarres::Mud::Driver::Compiler::Node::ArrAdd;
1031             sub convert {
1032 0     0     my ($self, @rest) = @_;
1033 0           my ($left, $right) = $self->values;
1034 0           my @errors = ();
1035 0 0         $self->check_children([ $left, $right ], @rest)
1036             or return $self->tc_fail(T_ARRAY);
1037             # This should use compatible() or can_promote() or something.
1038 0 0         $left->type->is_array
1039             or push(@errors, "LHS of array add is not an array");
1040 0 0         $right->type->is_array
1041             or push(@errors, "RHS of array add is not an array");
1042 0 0         return @errors if @errors;
1043 0           $self->settype($right->type->unify($right->type));
1044 0           return ();
1045             }
1046             }
1047              
1048             {
1049             package Anarres::Mud::Driver::Compiler::Node::ArrSub;
1050             sub convert {
1051 0     0     my ($self, @rest) = @_;
1052 0           my ($left, $right) = $self->values;
1053 0           my @errors = ();
1054 0 0         $self->check_children([ $left, $right ], @rest)
1055             or return $self->tc_fail(T_ARRAY);
1056             # This should use compatible() or can_promote() or something.
1057 0 0         $left->type->is_array
1058             or push(@errors, "LHS of array add is not an array");
1059 0 0         $right->type->is_array
1060             or push(@errors, "RHS of array add is not an array");
1061 0 0         return @errors if @errors;
1062 0           $self->settype($left->type);
1063 0           return ();
1064             }
1065             }
1066              
1067             {
1068             package Anarres::Mud::Driver::Compiler::Node::ArrOr;
1069             __PACKAGE__->steal("ArrAdd", "check");
1070             }
1071              
1072             {
1073             package Anarres::Mud::Driver::Compiler::Node::ArrAnd;
1074             __PACKAGE__->steal("ArrSub", "check");
1075             }
1076              
1077             {
1078             package Anarres::Mud::Driver::Compiler::Node::MapAdd;
1079             sub convert {
1080 0     0     my ($self, @rest) = @_;
1081 0           my ($left, $right) = $self->values;
1082 0           my @errors = ();
1083 0 0         $self->check_children([ $left, $right ], @rest)
1084             or return ("Failed to check children");
1085             # This should use compatible() or can_promote() or something.
1086 0 0         $left->type->is_mapping
1087             or push(@errors, "LHS of mapping add is not an mapping");
1088 0 0         $right->type->is_mapping
1089             or push(@errors, "RHS of mapping add is not an mapping");
1090 0 0         return @errors if @errors;
1091 0           $self->settype($right->type->unify($right->type));
1092 0           return ();
1093             }
1094             }
1095              
1096             {
1097             package Anarres::Mud::Driver::Compiler::Node::ExpComma;
1098             sub check {
1099 0     0     my ($self, @rest) = @_;
1100 0           my ($left, $right) = $self->values;
1101 0           $self->tc_start;
1102 0 0         $self->check_children([ $left, $right ], @rest)
1103             or return $self->tc_fail($right->type);
1104 0           $self->settype($right->type);
1105 0           return $self->tc_end;
1106             }
1107             }
1108              
1109             {
1110             package Anarres::Mud::Driver::Compiler::Node::ExpCond;
1111             sub check {
1112 0     0     my ($self, @rest) = @_;
1113 0           my ($cond, $left, $right) = $self->values;
1114 0           $self->tc_start;
1115 0 0         $self->check_children([ $cond, $left, $right ], @rest)
1116             or return $self->tc_fail;
1117             # XXX Check that cond is a boolean.
1118 0           $self->settype($right->type->unify($left->type));
1119 0           return $self->tc_end;
1120             }
1121             }
1122              
1123             {
1124             package Anarres::Mud::Driver::Compiler::Node::Block;
1125             # The funny thing about blocks is that no type information goes
1126             # into or out of them. If a subnode fails to check, it will
1127             # always fail to check. Therefore, if the block fails, there
1128             # is never any point in rechecking it. Since the fact of the
1129             # failure is already recorded, there is no point returning it
1130             # recursively from here. So we always call $self->tc_end.
1131             # XXX This is a caveat and should be noted in case we try to
1132             # do a fuller unification algorithm which infers types on
1133             # variables or closures. For this reason, we temporarily let
1134             # it fail.
1135             sub check {
1136 0     0     my ($self, $program, @rest) = @_;
1137 0           my $ret = 1;
1138              
1139 0           $self->tc_start;
1140              
1141 0           $program->save_locals;
1142 0           foreach (@{ $self->value(0) }) { # Local variables
  0            
1143 0           $program->local($_->name, $_);
1144             }
1145 0           foreach (@{ $self->value(1) }) { # Statements
  0            
1146 0 0         $_->check($program, @rest)
1147             or $ret = undef;
1148             }
1149 0           $program->restore_locals;
1150              
1151 0           $self->settype(T_VOID);
1152 0 0         return $ret ? $self->tc_end : $self->tc_fail(T_VOID);
1153             }
1154             }
1155              
1156             {
1157             package Anarres::Mud::Driver::Compiler::Node::StmtForeach;
1158             # This method does a lot of the common stuff for the two
1159             # 'subclasses'. I could alternatively use a 'choose' here...
1160             sub check {
1161 0     0     my ($self, $program, @rest) = @_;
1162 0           my $ret;
1163 0           $self->tc_start;
1164              
1165             # Actually, I can rebless before I check the children!
1166 0 0         if ($self->value(1)) { # Second lvalue
1167 0           bless $self, ref($self) . "Map";
1168             }
1169             else {
1170 0           bless $self, ref($self) . "Arr";
1171             }
1172 0           $self->settype(T_VOID);
1173              
1174 0           $self->idx_promote_to_block(3);
1175 0           my @values = $self->values;
1176 0 0         $self->check_children(\@values, $program, @rest)
1177             or return undef;
1178              
1179 0           return $self->check($program, @rest);
1180             }
1181             }
1182              
1183             {
1184             package Anarres::Mud::Driver::Compiler::Node::StmtForeachArr;
1185             sub check {
1186 0     0     my ($self, $program, @rest) = @_;
1187 0           my ($lv0, undef, $rv) = $self->values;
1188              
1189 0 0         unless ($lv0->lvaluep) {
1190 0           $program->error("foreach key lvalue must be an lvalue");
1191 0           return $self->tc_fail(T_VOID);
1192             }
1193              
1194             # Check that $rv->type->deref->compatible($lv0->type)
1195              
1196 0           return $self->tc_end;
1197             }
1198             }
1199              
1200             {
1201             package Anarres::Mud::Driver::Compiler::Node::StmtForeachMap;
1202             sub check {
1203 0     0     my ($self, $program, @rest) = @_;
1204 0           my ($lv0, $lv1, $rv) = $self->values;
1205              
1206 0 0         unless ($lv0->lvaluep) {
1207 0           $program->error("foreach key lvalue must be an lvalue");
1208 0           return $self->tc_fail(T_VOID);
1209             }
1210 0 0         unless ($lv0->type->equals(T_STRING)) {
1211 0           $program->error("foreach key lvalue must be type string");
1212 0           return $self->tc_fail(T_VOID);
1213             }
1214              
1215             # Check that $rv->type->deref->compatible($lv1->type)
1216              
1217 0           return $self->tc_end;
1218             }
1219             }
1220              
1221             {
1222             package Anarres::Mud::Driver::Compiler::Node::StmtSwitch;
1223             sub check {
1224 0     0     my ($self, $program, @rest) = @_;
1225 0           my ($exp, $block) = $self->values;
1226 0           my $ret = 1;
1227 0           $self->tc_start;
1228 0 0         $exp->check($program, @rest)
1229             or $ret = undef;
1230 0           my $tgt_break = $program->switch_start($exp->type);
1231 0           $self->setvalue(2, $tgt_break); # end of switch
1232 0 0         $block->check($program, @rest)
1233             or $ret = undef;
1234 0           my $data = $program->switch_end;
1235 0           $self->setvalue(3, $data->[0]); # labels
1236 0           $self->setvalue(4, $data->[1]); # default
1237 0           $self->settype(T_VOID);
1238 0 0         return $ret ? $self->tc_end : $self->tc_fail(T_VOID);
1239             }
1240             }
1241              
1242             {
1243             package Anarres::Mud::Driver::Compiler::Node::StmtCase;
1244             sub check {
1245 0     0     my ($self, $program, @rest) = @_;
1246 0           my $case = $self->value(0);
1247 0           $self->tc_start;
1248 0 0         $case->check($program, @rest)
1249             or return $self->tc_fail(T_VOID);
1250 0 0         unless ($case->constp) {
1251 0           $program->error("'case' value is not constant");
1252 0           return $self->tc_fail(T_VOID);
1253             }
1254 0           $self->setvalue(2, $program->label($case));
1255 0           $self->settype(T_VOID);
1256 0           return $self->tc_end;
1257             }
1258             }
1259              
1260             {
1261             package Anarres::Mud::Driver::Compiler::Node::StmtDefault;
1262             sub check {
1263 0     0     my ($self, $program, @rest) = @_;
1264 0           $self->tc_start;
1265 0           $self->setvalue(0, $program->default);
1266 0           $self->settype(T_VOID);
1267 0           return $self->tc_end;
1268             }
1269             }
1270              
1271             {
1272             package Anarres::Mud::Driver::Compiler::Node::StmtBreak;
1273             sub check {
1274 0     0     my ($self, $program, @rest) = @_;
1275 0           $self->tc_start;
1276 0           $self->setvalue(0, $program->getbreaktarget);
1277 0           $self->settype(T_VOID);
1278 0           return $self->tc_end;
1279             }
1280             }
1281              
1282             {
1283             package Anarres::Mud::Driver::Compiler::Node::StmtContinue;
1284             sub check {
1285 0     0     my ($self, $program, @rest) = @_;
1286 0           $self->tc_start;
1287             # XXX Do this.
1288             # $self->setvalue(0, $program->getbreaktarget);
1289 0           $self->settype(T_VOID);
1290 0           return $self->tc_end;
1291             }
1292             }
1293              
1294             {
1295             package Anarres::Mud::Driver::Compiler::Node::StmtIf;
1296             sub check {
1297 0     0     my ($self, $program, @rest) = @_;
1298 0           $self->tc_start;
1299              
1300 0           $self->idx_promote_to_block(1);
1301             # Allow the 'elsif' perlism.
1302 0 0 0       if ($self->value(2) and (ref($self->value(2)) !~ /::StmtIf$/)) {
1303             # Would it be better to do this in the code generator?
1304 0           $self->idx_promote_to_block(2);
1305             }
1306              
1307 0           my ($cond, $if, $else) = $self->values;
1308 0           my $ret = 1;
1309              
1310 0 0         $cond->check($program, @rest)
1311             or $ret = undef;
1312              
1313             # # Now we inspect $cond and set hints. However, this is wrong
1314             # # in the 'else' block!
1315             # if (ref($cond) =~ /::Funcall$/) {
1316             # my $method = $cond->value(0);
1317             # my $name = $method->name;
1318             # # intp, stringp, boolp, objectp, classp, arrayp, mapp
1319             # if ($name =~ /(?:int|string|bool|object|class|array|map)p/){
1320             # print "Hinting conditional: Call to $name\n";
1321             # }
1322             # }
1323              
1324 0 0         $if->check($program, @rest)
1325             or $ret = undef;
1326              
1327 0 0         if ($else) {
1328             # Reverse the hint
1329              
1330 0 0         $else->check($program, @rest)
1331             or $ret = undef;
1332             }
1333              
1334 0           $_[0]->settype(T_VOID);
1335 0 0         return $ret ? $self->tc_end : $self->tc_fail(T_VOID);
1336             }
1337             }
1338              
1339             {
1340             package Anarres::Mud::Driver::Compiler::Node::StmtReturn;
1341             sub check {
1342 0     0     my ($self, $program, @rest) = @_;
1343 0           $self->tc_start;
1344 0           my $val = $self->value(0);
1345 0 0         if ($val) {
1346 0 0         $val->check($program, @rest)
1347             or return $self->tc_fail(T_VOID);
1348             }
1349             # XXX Check that the returned type is compatible with the
1350             # function type.
1351 0           $self->settype(T_VOID);
1352 0           return $self->tc_end;
1353             }
1354             }
1355              
1356             {
1357             package Anarres::Mud::Driver::Compiler::Node::StmtTry;
1358             sub check {
1359 0     0     my ($self, $program, @rest) = @_;
1360 0           $self->tc_start;
1361 0           my @values = $self->values;
1362 0           my $ret = 1;
1363 0 0         $self->check_children(\@values, $program, @rest)
1364             or return $self->tc_fail(T_VOID);
1365 0 0         unless ($values[1]->lvaluep) {
1366 0           $program->error("'catch' lvalue must be an lvalue");
1367 0           return $self->tc_fail(T_VOID);
1368             }
1369 0           $self->settype(T_VOID);
1370 0 0         return $ret ? $self->tc_end : $self->tc_fail(T_VOID);
1371             }
1372             }
1373              
1374             # print STDERR Dumper(\%OPCHOICES);
1375              
1376             if (1) {
1377 3     3   23 use strict;
  3         8  
  3         183  
1378              
1379             my $package = __PACKAGE__;
1380             $package =~ s/::Check$/::Node/;
1381 3     3   16 no strict qw(refs);
  3         7  
  3         1129  
1382             my @missing;
1383             my @nochoice;
1384             my @nocode;
1385             my @spurious;
1386             my @oldcheck;
1387             foreach (@NODETYPES) {
1388             push(@oldcheck, $_) if defined &{"$package\::$_\::OLD_check"};
1389             my $tpt = $OPTYPES{$_};
1390             if ($tpt ne 'CODE') {
1391             push(@spurious, $_) if defined &{"$package\::$_\::check"};
1392             }
1393             next if ref($tpt) eq 'ARRAY';
1394             next if $tpt eq 'NOCHECK';
1395             if ($tpt eq 'CODE') {
1396             push(@nocode, $_) unless defined &{"$package\::$_\::check"};
1397             next;
1398             }
1399             if ($tpt eq 'CHOOSE') {
1400             push(@nochoice, $_) unless $OPCHOICES{$_};
1401             next;
1402             }
1403             push(@missing, $_);
1404             }
1405             print "OLD code for check in @oldcheck\n" if @oldcheck;
1406             print "Spurious code for check in @spurious\n" if @spurious;
1407             print "No code for check in @nocode\n" if @nocode;
1408             print "No choices for check in @nochoice\n" if @nochoice;
1409             print "No check in @missing\n" if @missing;
1410             }
1411              
1412             1;