package Parse::Eyapp::Scope;
use strict;
use warnings;
use Carp;
use Parse::Eyapp::Base qw(part valid_keys invalid_keys);
my %_new_scope = (
SCOPE_NAME => 'STRING',
ENTRY_NAME => 'STRING',
SCOPE_DEPTH => 'STRING',
);
my $valid_scope_keys = valid_keys(%_new_scope);
sub new {
my $class = shift;
my %args = @_;
if (defined($a = invalid_keys(\%_new_scope, \%args))) {
croak("Parse::Eyapp::Scope::new Error!:\n"
."unknown argument $a. Valid arguments for new are:\n $valid_scope_keys")
}
$args{ENTRY_NAME} = 'entry' unless defined($args{ENTRY_NAME});
$args{SCOPE_NAME} = 'scope' unless defined($args{SCOPE_NAME});
$args{SCOPE_DEPTH} = '' unless defined($args{SCOPE_DEPTH});
$args{PENDING_DECL} = [];
$args{SCOPE_MARK} = 0;
$args{DEPTH} = -1; # first depth is 0
bless \%args, $class;
}
sub begin_scope {
my $self = shift;
# Set the mark for next scope to the level of the stack of instances
$self->{SCOPE_MARK} = @{$self->{PENDING_DECL}};
# Save current mark in the stack of marks: it is an index
push @{$self->{SCOPE_STACK}}, $self->{SCOPE_MARK};
$self->{DEPTH}++; # new scope, new depth
}
####################################################################
# Usage : ($nondec, $declared) = $ids->end_scope($program->{symboltable}, $program, 'type');
# ($nondec, $declared) = $ids->end_scope($while);
# ($nondec, $declared) = $ids->end_scope($symbol_table, 'label');
#
sub end_scope {
my $self = shift; # The scope object
my $st; # reference to the hash holding the symbol table for this scope
my $block; # The node owning the current scope
# first arg can be the "block node" in which case the s.t. is omitted
if (UNIVERSAL::isa($_[0], 'Parse::Eyapp::Node')) {
# The call has the form: ($nondec, $declared) = $ids->end_scope($while);
$block = shift;
}
elsif (UNIVERSAL::isa($_[0], 'HASH')) {
# first arg can be the s.t. in which case the block node is expected
$st = shift;
if (UNIVERSAL::isa($_[0], 'Parse::Eyapp::Node')) {
$block = shift;
}
}
else {
croak "end_scope error: Specify a symbol table or a scope node\n"
}
# @_ = Remaining args hold key names for the entry that will be added to the instances
# Get the index pointing to the beginning of the current scope
my $scope = pop @{$self->{SCOPE_STACK}};
croak "Error: end_scope called without matching begin_scope\n" unless defined($scope);
# Get tyhe instances ocurring in this scope
my @instances = splice @{$self->{PENDING_DECL}}, $scope;
if (defined($st)) {
# Partitions @instance based on the return value of BLOCK
my ($nodeclared, $declared) = part { exists $st->{$_->key} } @instances;
$declared = [] unless $declared;
$nodeclared = [] unless $nodeclared;
# Return non declared identifiers to the "pending of declarations" queue
push @{$self->{PENDING_DECL}}, @$nodeclared;
# Set the scope attribute for those instances that were declared
for my $i (@$declared) {
next unless UNIVERSAL::isa($i, 'HASH');
$i->{$self->{SCOPE_NAME}} = $block if defined($block);
if (UNIVERSAL::can($i, 'key')) {
my $key = $i->key;
$i->{$self->{ENTRY_NAME}} = $st->{$key};
for (@_) {
# $_ must be a string and a key of %$st
if (ref($_)) {
warn "end_scope warning! expecting a string key for symbol table entry $key not a reference\n";
}
next unless exists $st->{$key}{$_};
$i->{$_} = $st->{$key}{$_};
}
}
}
$block->{$self->{SCOPE_DEPTH}} = $self->{DEPTH} if $self->{SCOPE_DEPTH};
$self->{DEPTH}--;
return wantarray? ($nodeclared, $declared): $nodeclared;
}
# Not symbol table: Simple scope
# Set the scope attribute for those instances that were declared
my @r;
$block->{$self->{SCOPE_NAME}} = \@r;
for my $i (@instances) {
$i->{$self->{SCOPE_NAME}} = $block;
push @r, $i;
}
$block->{$self->{SCOPE_DEPTH}} = $self->{DEPTH} if $self->{SCOPE_DEPTH};
$self->{DEPTH}--;
return \@instances;
}
# To be called for each ocurrence of an identifier
sub scope_instance {
my $self = shift;
my $NODE = shift;
push @{$self->{PENDING_DECL}}, $NODE;
}
1;
__END__