ljr/wcmtools/s2/S2/NodeForeachStmt.pm

135 lines
3.5 KiB
Perl
Executable File

#!/usr/bin/perl
#
package S2::NodeForeachStmt;
use strict;
use S2::Node;
use S2::NodeVarDecl;
use S2::NodeVarRef;
use S2::NodeExpr;
use S2::NodeStmtBlock;
use vars qw($VERSION @ISA);
$VERSION = '1.0';
@ISA = qw(S2::Node);
sub new {
my ($class) = @_;
my $n = new S2::Node;
bless $n, $class;
}
sub canStart {
my ($class, $toker) = @_;
return $toker->peek() == $S2::TokenKeyword::FOREACH
}
sub parse {
my ($class, $toker) = @_;
my $n = new S2::NodeForeachStmt;
$n->setStart($n->requireToken($toker, $S2::TokenKeyword::FOREACH));
if (S2::NodeVarDecl->canStart($toker)) {
$n->addNode($n->{'vardecl'} = S2::NodeVarDecl->parse($toker));
} else {
$n->addNode($n->{'varref'} = S2::NodeVarRef->parse($toker));
}
# expression in parenthesis representing an array to iterate over:
$n->requireToken($toker, $S2::TokenPunct::LPAREN);
$n->addNode($n->{'listexpr'} = S2::NodeExpr->parse($toker));
$n->requireToken($toker, $S2::TokenPunct::RPAREN);
# and what to do on each element
$n->addNode($n->{'stmts'} = S2::NodeStmtBlock->parse($toker));
return $n;
}
sub check {
my ($this, $l, $ck) = @_;
my $ltype = $this->{'listexpr'}->getType($ck);
if ($ltype->isHashOf()) {
$this->{'isHash'} = 1;
} elsif ($ltype->equals($S2::Type::STRING)) {
$this->{'isString'} = 1;
} elsif (! $ltype->isArrayOf()) {
S2::error($this, "Must use an array, hash, or string in a foreach");
}
my $itype;
if ($this->{'vardecl'}) {
$this->{'vardecl'}->populateScope($this->{'stmts'});
$itype = $this->{'vardecl'}->getType();
}
$itype = $this->{'varref'}->getType($ck) if $this->{'varref'};
if ($this->{'isHash'}) {
unless ($itype->equals($S2::Type::STRING) ||
$itype->equals($S2::Type::INT)) {
S2::error($this, "Foreach iteration variable must be a ".
"string or int when interating over the keys ".
"in a hash");
}
} elsif ($this->{'isString'}) {
unless ($itype->equals($S2::Type::STRING)) {
S2::error($this, "Foreach iteration variable must be a ".
"string when interating over the characters ".
"in a string");
}
} else {
# iter type must be the same as the list type minus
# the final array ref
# figure out the desired type
my $dtype = $ltype->clone();
$dtype->removeMod();
unless ($dtype->equals($itype)) {
S2::error("Foreach iteration variable is of type ".
$itype->toString . ", not the expected type of ".
$dtype->toString);
}
}
$ck->pushLocalBlock($this->{'stmts'});
$this->{'stmts'}->check($l, $ck);
$ck->popLocalBlock();
}
sub asS2 {
my ($this, $o) = @_;
die "unported";
}
sub asPerl {
my ($this, $bp, $o) = @_;
$o->tabwrite("foreach ");
$this->{'vardecl'}->asPerl($bp, $o) if $this->{'vardecl'};
$this->{'varref'}->asPerl($bp, $o) if $this->{'varref'};
if ($this->{'isHash'}) {
$o->write(" (keys %{");
} elsif ($this->{'isString'}) {
$o->write(" (S2::get_characters(");
} else {
$o->write(" (\@{");
}
$this->{'listexpr'}->asPerl($bp, $o);
if ($this->{'isString'}) {
$o->write(")) ");
} else {
$o->write("}) ");
}
$this->{'stmts'}->asPerl($bp, $o);
$o->newline();
}