use strict;
use warnings;

#
#           Interface Definition Language (OMG IDL CORBA v3.0)
#

package CORBA::IDL::Node;

our $VERSION = '2.63';

use UNIVERSAL;

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $parser = shift;
    my %attr = @_;
    my $self = \%attr;
    foreach (keys %attr) {
        unless (defined $self->{$_}) {
            delete $self->{$_};
        }
    }
    bless($self, $class);
    $self->_Init($parser);      # specialized or default
    return $self;
}

sub isa {
    return UNIVERSAL::isa(shift, 'CORBA::IDL::' . shift);
}

sub _Init {
    # default
}

sub configure {
    my $self = shift;
    my %attr = @_;
    while ( my ($key, $value) = each(%attr) ) {
        if (defined $value) {
            $self->{$key} = $value;
        }
    }
    return $self;
}

sub line_stamp {
    my $self = shift;
    my ($parser) = @_;
    $self->{filename} = $parser->YYData->{filename};
    $self->{lineno} = $parser->YYData->{lineno};
}

sub getRef {
    my $self = shift;
    my $class = ref $self;
    $class = substr $class, rindex($class, ':') + 1;
    if (exists $self->{full}) {
        if (       $class eq 'Module'
                or $class =~ /^Forward/ ) {
            return $self;
        }
        else {
            return $self->{full};
        }
    }
    else {
        return $self;
    }
}

sub getInheritance {
    my $self = shift;
    my @list = ();
    if (exists $self->{inheritance}) {
        if (exists $self->{inheritance}->{list_interface}) {
            push @list, @{$self->{inheritance}->{list_interface}};
        }
        if (exists $self->{inheritance}->{list_value}) {
            push @list, @{$self->{inheritance}->{list_value}};
        }
    }
    return @list;
}

sub getProperty {
    my $self = shift;
    my ($key) = @_;
    return undef unless (exists $self->{props});
    return undef unless (exists $self->{props}->{$key});
    return $self->{props}->{$key};
}

sub hasProperty {
    my $self = shift;
    my ($key) = @_;
    return 0 unless (exists $self->{props});
    return 0 unless (exists $self->{props}->{$key});
    return 1;
}

sub visit {
    my $self = shift;
    my $class = ref $self;
    my $visitor = shift;
    no strict 'refs';
    while ($class ne 'CORBA::IDL::Node') {
        my $func = 'visit' . substr($class, rindex($class, ':') + 1);
        if ($visitor->can($func)) {
            return $visitor->$func($self, @_);
        }
        $class = ${"$class\::ISA"}[0];
    }
    warn "Please implement a function 'visit",ref $self,"' in '",ref $visitor,"'.\n";
    return undef;
}

# deprecated in favor of 'visit'
sub visitName {
    my $self = shift;
    my $class = ref $self;
    my $visitor = shift;
    no strict 'refs';
    while ($class ne 'CORBA::IDL::Node') {
        my $func = 'visitName' . substr($class, rindex($class, ':') + 1);
        if ($visitor->can($func)) {
            return $visitor->$func($self, @_);
        }
        $class = ${"$class\::ISA"}[0];
    }
    warn "Please implement a function 'visitName",ref $self,"' in '",ref $visitor,"'.\n";
    return undef;
}

1;

#
#   3.5     OMG IDL Specification
#

package CORBA::IDL::Specification;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    my %hash;
    foreach my $export (@{$self->{list_decl}}) {
        if (ref $export) {
            unless (ref($export) =~ /^CORBA::IDL::Forward/) {
                if ($export->isa('Module')) {
                    $hash{$export->{full}} = 1;
                }
                else {  # TypeDeclarators, StateMembers, Attributes
                    foreach (@{$export->{list_decl}}) {
                        $hash{$_} = 1 if (defined $_);
                    }
                }
            }
        }
        else {
            $hash{$export} = 1;
        }
    }
    $self->{list_export} = [keys %hash];
    $parser->YYData->{symbtab}->Insert($self);
}

#
#   3.6     Import Declaration
#

package CORBA::IDL::Import;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $parser->YYData->{symbtab}->Import($self);
}

#
#   3.7     Module Declaration
#

package CORBA::IDL::Modules;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::Module;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc}
                unless (exists $self->{doc});
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->PushCurrentRoot($self);
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my $defn = $parser->YYData->{symbtab}->Lookup($self->{full});   # Modules
    my %hash;
    foreach my $module (@{$defn->{list_decl}}) {
        foreach my $export (@{$module->{list_decl}}) {
            if (ref $export) {
                unless (ref($export) =~ /^CORBA::IDL::Forward/) {
                    if ($export->isa('Module')) {
                        $hash{$export->{full}} = 1;
                    }
                    else {  # TypeDeclarators, StateMembers, Attributes
                        foreach (@{$export->{list_decl}}) {
                            $hash{$_} = 1 if (defined $_);
                        }
                    }
                }
            }
            else {
                $hash{$export} = 1;
            }
        }
    }
    $defn->{list_export} = [keys %hash];
    return $defn;
}

#
#   3.8     Interface Declaration
#

package CORBA::IDL::BaseInterface;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $self->{local_type} = 1 if ($self->isa('LocalInterface'));
    $parser->YYData->{symbtab}->PushCurrentScope($self);
    $parser->YYData->{curr_itf} = $self;
    $self->_CheckInheritance($parser);          # specialized
    $self->_InsertInherited($parser);
    $parser->YYData->{curr_node} = $self;
}

sub _InsertInherited {
    my $self = shift;
    my ($parser) = @_;
    $self->{hash_attribute_operation} = {};
    foreach ($self->getInheritance()) {
        my $base = $parser->YYData->{symbtab}->Lookup($_);
        foreach (keys %{$base->{hash_attribute_operation}}) {
            my $name = $base->{hash_attribute_operation}{$_};
            my $defn = $parser->YYData->{symbtab}->Lookup($name);
            next if ($defn->isa('Initializer'));
            next if ($defn->isa('StateMember'));
#           next if ($defn->isa('Factory'));
#           next if ($defn->isa('Finder'));
            if (exists $self->{hash_attribute_operation}{$_}) {
                if ($self->{hash_attribute_operation}{$_} ne $name) {
                    $parser->Error("multi inheritance of '$_'.\n");
                }
            }
            else {
                $self->{hash_attribute_operation}{$_} = $name;
                $parser->YYData->{symbtab}->InsertInherit($self, $_, $name);
            }
        }
    }
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my @list;
    foreach my $export (@{$self->{list_decl}}) {
        if (ref $export) {
            unless (ref($export) =~ /^CORBA::IDL::Forward/) {
                foreach (@{$export->{list_decl}}) {
                    push @list, $_ if (defined $_);
                }
            }
        }
        else {
            push @list, $export;
        }
    }
    $self->{list_export} = \@list;
    $self->_CheckLocal($parser);            # specialized
    $self->_CheckNative($parser);           # specialized
    return $self;
}

sub _CheckNative {
    # If a native type is used as an exception for an operation, the
    # operation must appear in either a local interface or a valuetype.
}

sub Lookup {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    $class = substr $class, rindex($class, ':') + 1;
    my ($parser, $name, $bypass) = @_;
    my $defn = $parser->YYData->{symbtab}->Lookup($name);
    if (defined $defn) {
        if ($defn->isa('Forward' . $class)) {
            $parser->Error("'$name' is declared, but not defined.\n")
                    unless ($bypass);
        }
        elsif (! $defn->isa($class)) {
            $parser->Error("'$name' is not a $class.\n");
        }
        return $defn->{full};
    }
    else {
        return q{};
    }
}

#
#   3.8.2   Interface Inheritance Specification
#

package CORBA::IDL::InheritanceSpec;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{hash_interface} = {};
    my %hash;
    # 3.8.5 Interface Inheritance
    if (exists $self->{list_interface}) {
        foreach my $name (@{$self->{list_interface}}) {
            if (exists $hash{$name}) {
                $parser->Warning("'$name' redeclares inheritance.\n");
            }
            else {
                $hash{$name} = 1;
                $self->{hash_interface}->{$name} = 1;
                my $base = $parser->YYData->{symbtab}->Lookup($name);
                if (exists $base->{inheritance}) {
                    foreach (keys %{$base->{inheritance}->{hash_interface}}) {
                        $self->{hash_interface}->{$_} = 1;
                    }
                }
            }
        }
    }
    # 3.9.5 Valuetype Inheritance
    if (exists $self->{list_value}) {
        foreach my $name (@{$self->{list_value}}) {
            if (exists $hash{$name}) {
                $parser->Warning("'$name' redeclares inheritance.\n");
            }
            else {
                $hash{$name} = 1;
                $self->{hash_interface}->{$name} = 1;
                my $base = $parser->YYData->{symbtab}->Lookup($name);
                if (exists $base->{inheritance}) {
                    foreach (keys %{$base->{inheritance}->{hash_interface}}) {
                        $self->{hash_interface}->{$_} = 1;
                    }
                }
            }
        }
    }
}

package CORBA::IDL::Interface;

use base qw(CORBA::IDL::BaseInterface);

package CORBA::IDL::RegularInterface;

use base qw(CORBA::IDL::Interface);

sub _CheckInheritance {
    my $self = shift;
    my ($parser) = @_;
    if (exists $self->{inheritance}) {
        foreach (@{$self->{inheritance}->{list_interface}}) {
            my $base = $parser->YYData->{symbtab}->Lookup($_);
            # An unconstrained interface may not inherit from a local interface.
            if ($base->isa('LocalInterface')) {
                $parser->Error("'$self->{idf}' is not local.\n");
            }
        }
    }
}

sub _CheckLocal {
    my $self = shift;
    my ($parser) = @_;

    # A local type may not appear as a parameter, attribute, return type, or exception
    # declaration of an unconstrained interface or as a state member of a valuetype.
    foreach (@{$self->{list_export}}) {
        my $defn = $parser->YYData->{symbtab}->Lookup($_);
        if      ($defn->isa('Attribute')) {
            if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $defn->{type})) {
                $parser->Error("'$self->{idf}' is not local.\n");
            }
        }
        elsif ($defn->isa('Operation')) {
            if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $defn->{type})) {
                $parser->Error("'$self->{idf}' is not local.\n");
            }
            foreach (@{$defn->{list_param}}) {
                if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $_->{type})) {
                    $parser->Error("'$self->{idf}' is not local.\n");
                }
            }
        }
    }
}

sub _CheckNative {
    my $self = shift;
    my ($parser) = @_;

    # If a native type is used as an exception for an operation, the
    # operation must appear in either a local interface or a valuetype.
    foreach (@{$self->{list_export}}) {
        my $defn = $parser->YYData->{symbtab}->Lookup($_);
        if (exists $defn->{list_raise}) {
            foreach (@{$defn->{list_raise}}) {
                my $except = $parser->YYData->{symbtab}->Lookup($_);
                if ($except->isa('NativeType')) {
                    $parser->Error("'$except->{idf}' used in a not local interface.\n");
                }
            }
        }
    }
}

#
#   3.8.4   Forward Declaration
#

package CORBA::IDL::ForwardBaseInterface;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    $self->{local_type} = 1 if ($self->isa('ForwardLocalInterface'));
    $parser->YYData->{symbtab}->InsertForward($self);
}

package CORBA::IDL::ForwardInterface;

use base qw(CORBA::IDL::ForwardBaseInterface);

package CORBA::IDL::ForwardRegularInterface;

use base qw(CORBA::IDL::ForwardInterface);

package CORBA::IDL::ForwardAbstractInterface;

use base qw(CORBA::IDL::ForwardInterface);

package CORBA::IDL::ForwardLocalInterface;

use base qw(CORBA::IDL::ForwardInterface);

#
#   3.8.6   Abstract Interface
#

package CORBA::IDL::AbstractInterface;

use base qw(CORBA::IDL::Interface);

sub _CheckInheritance {
    my $self = shift;
    my ($parser) = @_;
    if (exists $self->{inheritance}) {
        foreach (@{$self->{inheritance}->{list_interface}}) {
            my $base = $parser->YYData->{symbtab}->Lookup($_);
            # (An unconstrained interface may not inherit from a local interface.)
            # An abstract interface may only inherit from other abstract interfaces.
            unless ($base->isa('AbstractInterface')) {
                $parser->Error("'$_' is not abstract.\n");
            }
        }
    }
}

sub _CheckLocal {
    my $self = shift;
    my ($parser) = @_;

    # A local type may not appear as a parameter, attribute, return type, or exception
    # declaration of an unconstrained interface or as a state member of a valuetype.
    foreach (@{$self->{list_export}}) {
        my $defn = $parser->YYData->{symbtab}->Lookup($_);
        if      ($defn->isa('Attribute')) {
            if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $defn->{type})) {
                $parser->Error("'$self->{idf}' is not local.\n");
            }
        }
        elsif ($defn->isa('Operation')) {
            if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $defn->{type})) {
                $parser->Error("'$self->{idf}' is not local.\n");
            }
            foreach (@{$defn->{list_param}}) {
                if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $_->{type})) {
                    $parser->Error("'$self->{idf}' is not local.\n");
                }
            }
        }
    }
}

sub _CheckNative {
    my $self = shift;
    my ($parser) = @_;

    # If a native type is used as an exception for an operation, the
    # operation must appear in either a local interface or a valuetype.
    foreach (@{$self->{list_export}}) {
        my $defn = $parser->YYData->{symbtab}->Lookup($_);
        if (exists $defn->{list_raise}) {
            foreach (@{$defn->{list_raise}}) {
                my $except = $parser->YYData->{symbtab}->Lookup($_);
                if ($except->isa('NativeType')) {
                    $parser->Error("'$except->{idf}' used in a not local interface.\n");
                }
            }
        }
    }
}

#
#   3.8.7   Local Interface
#

package CORBA::IDL::LocalInterface;

use base qw(CORBA::IDL::Interface);

sub _CheckInheritance {
    # A local interface may inherit from other local or unconstrained interfaces
}

sub _CheckLocal {
    # Any IDL type, including an unconstrained interface, may appear as a parameter,
    # attribute, return type, or exception declaration of a local interface.

    # A local type may be used as a parameter, attribute, return type, or exception
    # declaration of a local interface or of a valuetype.
}

#
#   3.9     Value Declaration
#

package CORBA::IDL::Value;

use base qw(CORBA::IDL::BaseInterface);

#   3.9.1   Regular Value Type
#

package CORBA::IDL::RegularValue;

use base qw(CORBA::IDL::Value);

sub _CheckInheritance {
    my $self = shift;
    my ($parser) = @_;
    if (exists $self->{inheritance}) {
        if (    exists $self->{inheritance}->{modifier}     # truncatable
            and exists $self->{modifier} ) {                # custom
            $parser->Error("'truncatable' is used in a custom value.\n");
        }
        if (exists $self->{inheritance}->{list_interface}) {
            my $nb = 0;
            foreach (@{$self->{inheritance}->{list_interface}}) {
                my $base = $parser->YYData->{symbtab}->Lookup($_);
                if ($base->isa('RegularInterface')) {
                    $nb ++;
                }
            }
            $parser->Error("'$self->{idf}' inherits from more than once regular interface.\n")
                    if ($nb > 1);
        }
        if (exists $self->{inheritance}->{list_value}) {
            my $nb = 0;
            foreach (@{$self->{inheritance}->{list_value}}) {
                my $base = $parser->YYData->{symbtab}->Lookup($_);
                if ($base->isa('RegularValue')) {
                    $nb ++;
                }
                if ($base->isa('BoxedValue')) {
                    $parser->Error("'$_' is a boxed value.\n")
                }
            }
            $parser->Error("'$self->{idf}' inherits from more than once regular value.\n")
                    if ($nb > 1);
        }
    }
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->SUPER::Configure($parser, @_);
    my @list;
    foreach my $value_element (@{$self->{list_decl}}) {
        next unless (ref $value_element eq 'CORBA::IDL::StateMembers');
        foreach (@{$value_element->{list_decl}}) {
            push @list, $_;
            $self->{deprecated} = 1 if (CORBA::IDL::TypeDeclarator->IsDeprecated($parser, $_));
        }
    }
    $self->configure(list_member    =>  \@list);    # list of 'StateMember'
    return $self;
}

sub _CheckLocal {
    # A local type may be used as a parameter, attribute, return type, or exception
    # declaration of a local interface or of a valuetype.
}

#
#   3.9.1.4 State Members
#

package CORBA::IDL::StateMembers;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    CORBA::IDL::TypeDeclarator->CheckDeprecated($parser, $self->{type});
    CORBA::IDL::TypeDeclarator->CheckForward($parser, $self->{type});
    my @list;
    foreach (@{$self->{list_expr}}) {
        my $member;
        my @array_size = @{$_};
        my $idf = shift @array_size;
        if (@array_size) {
            $member = new CORBA::IDL::StateMember($parser,
                    declspec        =>  $self->{declspec},
                    props           =>  $self->{props},
                    modifier        =>  $self->{modifier},
                    type            =>  $self->{type},
                    idf             =>  $idf,
                    array_size      =>  \@array_size,
                    deprecated      =>  1,
            );
            $parser->Deprecated("Anonymous type (array).\n")
                    if ($CORBA::IDL::Parser::IDL_VERSION ge '2.4');
        }
        else {
            $member = new CORBA::IDL::StateMember($parser,
                    declspec        =>  $self->{declspec},
                    props           =>  $self->{props},
                    modifier        =>  $self->{modifier},
                    type            =>  $self->{type},
                    idf             =>  $idf,
                    deprecated      =>  CORBA::IDL::TypeDeclarator->IsDeprecated($parser, $self->{type}),
            );
        }
        push @list, $member->{full};
    }
    $self->configure(list_decl  =>  \@list);
    # A local type may not appear as a parameter, attribute, return type, or exception
    # declaration of an unconstrained interface or as a state member of a valuetype.
    if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $self->{type})) {
        my $idf = $self->{type}->{idf} if (exists $self->{type}->{idf});
        $idf ||= $self->{type};
        $parser->Error("'$idf' is local.\n");
    }
}

package CORBA::IDL::StateMember;            # modifier, idf, type[, array_size]

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $parser->YYData->{symbtab}->Insert($self);
    if (defined $parser->YYData->{curr_itf}) {
        $parser->YYData->{curr_itf}->{hash_attribute_operation}{$self->{idf}} = $self->{full}
    }
    else {
        $parser->Error(__PACKAGE__ . "::new ERROR_INTERNAL.\n");
    }
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{curr_node} = $self;
}

#
#   3.9.1.5 Initializers
#

package CORBA::IDL::Initializer;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $parser->YYData->{symbtab}->Insert($self);
    $parser->YYData->{unnamed_symbtab} = new CORBA::IDL::UnnamedSymbtab($parser);
    if (defined $parser->YYData->{curr_itf}) {
        $self->{itf} = $parser->YYData->{curr_itf}->{full};
        $parser->YYData->{curr_itf}->{hash_attribute_operation}{$self->{idf}} = $self->{full}
    }
    else {
        $parser->Error(__PACKAGE__ . "::new ERROR_INTERNAL.\n");
    }
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my @list_in = ();
    foreach ( @{$self->{list_param}} ) {
        if      ($_->{attr} eq 'in') {
            unshift @list_in, $_;
        }
    }
    $self->{list_in} = \@list_in;
    $self->{list_inout} = [];
    $self->{list_out} = [];
    return $self;
}

#
#   3.9.2   Boxed Value Type
#
package CORBA::IDL::BoxedValue;

use base qw(CORBA::IDL::Value);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->PushCurrentScope($self);
    $parser->YYData->{curr_itf} = $self;
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my $type = CORBA::IDL::TypeDeclarator->GetDefn($parser, $self->{type});
    if ($type->isa('Value')) {
        if ($CORBA::IDL::Parser::IDL_VERSION ge '3.0') {
            $parser->Error("$self->{type}->{idf} is a value type.\n");
        }
        else {
            $parser->Info("$self->{type}->{idf} is a value type.\n");
        }
    }
    $self->{deprecated} = 1 if (CORBA::IDL::TypeDeclarator->IsDeprecated($parser, $type));
    return $self;
}

#
#   3.9.3   Abstract Value Type
#

package CORBA::IDL::AbstractValue;

use base qw(CORBA::IDL::Value);

sub _CheckInheritance {
    my $self = shift;
    my ($parser) = @_;
    if (exists $self->{inheritance}) {
        if (exists $self->{inheritance}->{list_interface}) {
            my $nb = 0;
            foreach (@{$self->{inheritance}->{list_interface}}) {
                my $base = $parser->YYData->{symbtab}->Lookup($_);
                if ($base->isa('RegularInterface')) {
                    $nb ++;
                }
            }
            $parser->Error("'$self->{idf}' inherits from more than once regular interface.\n")
                    if ($nb > 1);
        }
        if (exists $self->{inheritance}->{list_value}) {
            foreach (@{$self->{inheritance}->{list_value}}) {
                my $base = $parser->YYData->{symbtab}->Lookup($_);
                unless ($base->isa('AbstractValue')) {
                    $parser->Error("'$_' is not abstract value.\n");
                }
            }
        }
    }
}

sub _CheckLocal {
    # A local type may be used as a parameter, attribute, return type, or exception
    # declaration of a local interface or of a valuetype.
}

#
#   3.9.4   Value Forward Declaration
#

package CORBA::IDL::ForwardValue;

use base qw(CORBA::IDL::ForwardBaseInterface);

package CORBA::IDL::ForwardRegularValue;

use base qw(CORBA::IDL::ForwardValue);

package CORBA::IDL::ForwardAbstractValue;

use base qw(CORBA::IDL::ForwardValue);

#
#   3.10        Constant Declaration
#

package CORBA::IDL::Expression;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    if (      ! exists $self->{type} ) {
        $self->configure(
                type    =>  new CORBA::IDL::IntegerType($parser,
                                    value   =>  'unsigned long',
                                    auto    =>  1
                            )
        );
    }
    elsif (   @{$self->{list_expr}} == 1
            and defined $self->{list_expr}[0] ) {
        if (ref $self->{type}) {
            my $expr = $self->{list_expr}[0];
            if (      $self->{type}->isa('WideCharType')
                    and $expr->isa('CharacterLiteral') ) {
                $self->{list_expr} = [
                        new CORBA::IDL::WideCharacterLiteral($parser,
                                value   =>  $expr->{value}
                        )
                ];
            }
            elsif (   $self->{type}->isa('WideStringType')
                    and $expr->isa('StringLiteral') ) {
                $self->{list_expr} = [
                        new CORBA::IDL::WideStringLiteral($parser,
                                value   =>  $expr->{value}
                        )
                ];
            }
        }
    }
    $self->configure(
            value   =>  $self->Eval($parser)
    );
}

use Math::BigInt;
use Math::BigFloat;

use constant UCHAR_MAX      => new Math::BigInt(                 '255');
use constant SHRT_MIN       => new Math::BigInt(              '-32768');
use constant SHRT_MAX       => new Math::BigInt(               '32767');
use constant USHRT_MAX      => new Math::BigInt(               '65535');
use constant LONG_MIN       => new Math::BigInt(         '-2147483648');
use constant LONG_MAX       => new Math::BigInt(          '2147483647');
use constant ULONG_MAX      => new Math::BigInt(          '4294967295');
use constant LLONG_MIN      => new Math::BigInt('-9223372036854775808');
use constant LLONG_MAX      => new Math::BigInt( '9223372036854775807');
use constant ULLONG_MAX     => new Math::BigInt('18446744073709551615');
use constant FLT_MAX        => new Math::BigFloat(         '3.40282347e+38' );
use constant DBL_MAX        => new Math::BigFloat('1.79769313486231571e+308');
use constant LDBL_MAX       => new Math::BigFloat('1.79769313486231571e+308');
use constant FLT_MIN        => new Math::BigFloat(         '1.17549435e-38' );
use constant DBL_MIN        => new Math::BigFloat('2.22507385850720138e-308');
use constant LDBL_MIN       => new Math::BigFloat('2.22507385850720138e-308');

sub Eval {
    my $self = shift;
    my ($parser) = @_;
    my @list_expr = @{$self->{list_expr}};      # create a copy
    my $type = CORBA::IDL::TypeDeclarator->GetEffectiveType($parser, $self->{type});
    if (defined $type) {
        return _Eval($parser, $type, \@list_expr);
    }
    else {
        return 0;
    }
}

sub _EvalBinop {
    my ($parser, $type, $elt, $list_expr, $bypass) = @_;
    if (       $type->isa('IntegerType')
            or $type->isa('OctetType') ) {
        my $right = _Eval($parser, $type, $list_expr, 1);
        return undef unless (defined $right);
        my $left = _Eval($parser, $type, $list_expr, 1);
        return undef unless (defined $left);
        my $value = new Math::BigInt($left);
        if (    $elt->{op} eq '|' ) {
            $value->bior($right);
            return _CheckRange($parser, $type, $value, $bypass);
        }
        elsif ( $elt->{op} eq '^' ) {
            $value->bxor($right);
            return _CheckRange($parser, $type, $value, $bypass);
        }
        elsif ( $elt->{op} eq '&' ) {
            $value->band($right);
            return _CheckRange($parser, $type, $value, $bypass);
        }
        elsif ( $elt->{op} eq '+' ) {
            $value->badd($right);
            return _CheckRange($parser, $type, $value, $bypass);
        }
        elsif ( $elt->{op} eq '-' ) {
            $value->bsub($right);
            return _CheckRange($parser, $type, $value, $bypass);
        }
        elsif ( $elt->{op} eq '*' ) {
            $value->bmul($right);
            return _CheckRange($parser, $type, $value, $bypass);
        }
        elsif ( $elt->{op} eq '/' ) {
            $value->bdiv($right);
            return _CheckRange($parser, $type, $value, $bypass);
        }
        elsif ( $elt->{op} eq '%' ) {
            $value->bmod($right);
            return _CheckRange($parser, $type, $value, $bypass);
        }
        elsif ( $elt->{op} eq '>>' ) {
            if (0 <= $right and $right < 64) {
                $value->brsft($right);
                return _CheckRange($parser, $type, $value, $bypass);
            }
            else {
                $parser->Error("shift operation out of range.\n");
                return undef;
            }
        }
        elsif ( $elt->{op} eq '<<' ) {
            if (0 <= $right and $right < 64) {
                $value->blsft($right);
                return _CheckRange($parser, $type, $value, $bypass);
            }
            else {
                $parser->Error("shift operation out of range.\n");
                return undef;
            }
        }
        else {
            $parser->Error("_BinopEval (int) : INTERNAL ERROR.\n");
            return undef;
        }
    }
    elsif (  $type->isa('FloatingPtType') ) {
        my $right = _Eval($parser, $type, $list_expr);
        return undef unless (defined $right);
        my $left = _Eval($parser, $type, $list_expr);
        return undef unless (defined $left);
        my $value = new Math::BigFloat($left);
        if (    $elt->{op} eq '+' ) {
            $value->fadd($right);
            return _CheckRange($parser, $type, $value);
        }
        elsif ( $elt->{op} eq '-' ) {
            $value->fsub($right);
            return _CheckRange($parser, $type, $value);
        }
        elsif ( $elt->{op} eq '*' ) {
            $value->fmul($right);
            return _CheckRange($parser, $type, $value);
        }
        elsif ( $elt->{op} eq '/' ) {
            $value->fdiv($right);
            return _CheckRange($parser, $type, $value);
        }
        elsif (  $elt->{op} eq '|'
                or $elt->{op} eq '^'
                or $elt->{op} eq '&'
                or $elt->{op} eq '>>'
                or $elt->{op} eq '<<'
                or $elt->{op} eq '%' ) {
            $parser->Error("'$elt->{op}' is not valid for '$type'.\n");
        }
        else {
            $parser->Error("_EvalBinop (fp) : INTERNAL ERROR.\n");
            return undef;
        }
    }
    elsif (  $type->isa('FixedPtConstType') ) {
        my $right = _Eval($parser, $type, $list_expr);
        return undef unless (defined $right);
        my $left = _Eval($parser, $type, $list_expr);
        return undef unless (defined $left);
        my $value = new Math::BigFloat($left);
        if (    $elt->{op} eq '+' ) {
            $value->fadd($right);
            return _CheckRange($parser, $type, $value);
        }
        elsif ( $elt->{op} eq '-' ) {
            $value->fsub($right);
            return _CheckRange($parser, $type, $value);
        }
        elsif ( $elt->{op} eq '*' ) {
            $value->fmul($right);
            return _CheckRange($parser, $type, $value);
        }
        elsif ( $elt->{op} eq '/' ) {
            $value->fdiv($right);
            return _CheckRange($parser, $type, $value);
        }
        elsif (  $elt->{op} eq '|'
                or $elt->{op} eq '^'
                or $elt->{op} eq '&'
                or $elt->{op} eq '>>'
                or $elt->{op} eq '<<'
                or $elt->{op} eq '%' ) {
            $parser->Error("'$elt->{op}' is not valid for '$type'.\n");
            return undef;
        }
        else {
            $parser->Error("_EvalBinop (fixed) : INTERNAL ERROR.\n");
            return undef;
        }
    }
    else {
        $parser->Error("'$type->{value}' can't use expression.\n");
        return undef;
    }
}

sub _EvalUnop {
    my ($parser, $type, $elt, $list_expr, $bypass) = @_;
    if (       $type->isa('IntegerType')
            or $type->isa('OctetType') ) {
        my $right = _Eval($parser, $type, $list_expr, 1);
        return undef unless (defined $right);
        my $value = new Math::BigInt($right);
        if (    $elt->{op} eq '+' ) {
            return _CheckRange($parser, $type, $right, $bypass);
        }
        elsif ( $elt->{op} eq '-' ) {
            $value->bneg();
            return _CheckRange($parser, $type, $value, $bypass);
        }
        elsif ( $elt->{op} eq '~' ) {
            my $cpl;
            if    ($type->{value} eq 'short') {
                $cpl = USHRT_MAX;
            }
            elsif ($type->{value} eq 'unsigned short') {
                $cpl = USHRT_MAX;
            }
            elsif ($type->{value} eq 'long') {
                $cpl = ULONG_MAX;
            }
            elsif ($type->{value} eq 'unsigned long') {
                $cpl = ULONG_MAX;
            }
            elsif ($type->{value} eq 'long long') {
                $cpl = ULLONG_MAX;
            }
            elsif ($type->{value} eq 'unsigned long long') {
                $cpl = ULLONG_MAX;
            }
            elsif ($type->{value} eq 'octet') {
                $cpl = UCHAR_MAX;
            }
            $value->bxor($cpl);
            return _CheckRange($parser, $type, $value, $bypass);
        }
        else {
            $parser->Error("_EvalUnop (int) : INTERNAL ERROR.\n");
            return undef;
        }
    }
    elsif (  $type->isa('FloatingPtType') ) {
        my $right = _Eval($parser, $type, $list_expr);
        return undef unless (defined $right);
        my $value = new Math::BigFloat($right);
        if (    $elt->{op} eq '+' ) {
            return _CheckRange($parser, $type, $right);
        }
        elsif ( $elt->{op} eq '-' ) {
            $value->fneg();
            return _CheckRange($parser, $type, $value);
        }
        elsif (  $elt->{op} eq '~' ) {
            $parser->Error("'$elt->{op}' is not valid for '$type'.\n");
            return undef;
        }
        else {
            $parser->Error("_EvalUnop (fp) : INTERNAL ERROR.\n");
            return undef;
        }
    }
    elsif (  $type->isa('FixedPtConstType') ) {
        my $right = _Eval($parser, $type, $list_expr);
        return undef unless (defined $right);
        my $value = new Math::BigFloat($right);
        if (    $elt->{op} eq '+' ) {
            return _CheckRange($parser, $type, $right);
        }
        elsif ( $elt->{op} eq '-' ) {
            $value->fneg();
            return _CheckRange($parser, $type, $value);
        }
        elsif (  $elt->{op} eq '~' ) {
            $parser->Error("'$elt->{op}' is not valid for '$type'.\n");
            return undef;
        }
        else {
            $parser->Error("_EvalUnop (fixed) : INTERNAL ERROR.\n");
            return undef;
        }
    }
    else {
        $parser->Error("'$type->{value}' can't use expression.\n");
        return undef;
    }
}

sub _Eval {
    my ($parser, $type, $list_expr, $bypass) = @_;
    my $elt = pop @$list_expr;
    return undef unless (defined $elt);
    return undef unless ($elt);
    unless (ref $elt) {
        $elt = $parser->YYData->{symbtab}->Lookup($elt);
        return undef unless (defined $elt);
    }
    if    ($elt->isa('BinaryOp'))   {
        return _EvalBinop($parser, $type, $elt, $list_expr, $bypass);
    }
    elsif ($elt->isa('UnaryOp')) {
        return _EvalUnop($parser, $type, $elt, $list_expr, $bypass);
    }
    elsif ($elt->isa('Constant')) {
        if (ref $type eq ref $elt->{value}->{type}) {
            return _CheckRange($parser, $type, $elt->{value}->{value}, $bypass);
        }
        elsif ($type->isa('IntegerType') and $elt->{value}->{type}->isa('OctetType')) {
            return _CheckRange($parser, $type, $elt->{value}->{value}, $bypass);
        }
        else {
            $parser->Error("'$elt->{value}->{value}' is not a '$type->{value}'.\n");
            return undef;
        }
    }
    elsif ($elt->isa('Enum')) {
        if ($type eq $parser->YYData->{symbtab}->Lookup($elt->{type})) {
            return $elt;
        }
        else {
            $parser->Error("'$elt->{idf}' is not a '$type->{idf}'.\n");
            return undef;
        }
    }
    elsif ($elt->isa('IntegerLiteral')) {
        if ($type->isa('IntegerType')) {
            return _CheckRange($parser, $type, $elt->{value}, $bypass);
        }
        elsif ($type->isa('OctetType')) {
            return _CheckRange($parser, $type, $elt->{value}, $bypass);
        }
        else {
            $parser->Error("'$elt->{value}' is not a '$type->{value}'.\n");
            return undef;
        }
    }
    elsif ($elt->isa('StringLiteral')) {
        if ($type->isa('StringType')) {
            return _CheckRange($parser, $type, $elt->{value});
        }
        else {
            $parser->Error("'$elt->{value}' is not a '$type->{value}'.\n");
            return undef;
        }
    }
    elsif ($elt->isa('WideStringLiteral')) {
        if ($type->isa('WideStringType')) {
            return _CheckRange($parser, $type, $elt->{value});
        }
        else {
            $parser->Error("'$elt->{value}' is not a '$type->{value}'.\n");
            return undef;
        }
    }
    elsif ($elt->isa('CharacterLiteral')) {
        if ($type->isa('CharType')) {
            return $elt->{value};
        }
        else {
            $parser->Error("'$elt->{value}' is not a '$type->{value}'.\n");
            return undef;
        }
    }
    elsif ($elt->isa('WideCharacterLiteral')) {
        if ($type->isa('WideCharType')) {
            return $elt->{value};
        }
        else {
            $parser->Error("'$elt->{value}' is not a '$type->{value}'.\n");
            return undef;
        }
    }
    elsif ($elt->isa('FixedPtLiteral')) {
        if ($type->isa('FixedPtConstType')) {
            return _CheckRange($parser, $type, $elt->{value});
        }
        else {
            $parser->Error("'$elt->{value}' is not a '$type->{value}'.\n");
            return undef;
        }
    }
    elsif ($elt->isa('FloatingPtLiteral')) {
        if ($type->isa('FloatingPtType')) {
            return _CheckRange($parser, $type, $elt->{value});
        }
        else {
            $parser->Error("'$elt->{value}' is not a '$type->{value}'.\n");
            return undef;
        }
    }
    elsif ($elt->isa('BooleanLiteral')) {
        if ($type->isa('BooleanType')) {
            return $elt->{value};
        }
        else {
            $parser->Error("'$elt->{value}' is not a '$type->{value}'.\n");
            return undef;
        }
    }
    else {
        $parser->Error("_Eval: INTERNAL ERROR ",ref $elt," .\n");
        return undef;
    }
}

sub _CheckRange {
    my ($parser, $type, $value, $bypass) = @_;
    return $value if (defined $bypass);
    if (       $type->isa('IntegerType') ) {
        if (   $type->{value} eq 'short' ) {
            if ($value >= SHRT_MIN and $value <= SHRT_MAX) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' $value is out of range.\n");
                return undef;
            }
        }
        elsif ($type->{value} eq 'long') {
            if ($value >= LONG_MIN and $value <= LONG_MAX) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' $value is out of range.\n");
                return undef;
            }
        }
        elsif ($type->{value} eq 'long long') {
            if ($value >= LLONG_MIN and $value <= LLONG_MAX) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' $value is out of range.\n");
                return undef;
            }
        }
        elsif ($type->{value} eq 'unsigned short') {
            if ($value >= 0 and $value <= USHRT_MAX) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' $value is out of range.\n");
                return undef;
            }
        }
        elsif ($type->{value} eq 'unsigned long') {
            if ($value >= 0 and $value <= ULONG_MAX) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' $value is out of range.\n");
                return undef;
            }
        }
        elsif ($type->{value} eq 'unsigned long long') {
            if ($value >= 0 and $value <= ULLONG_MAX) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' $value is out of range.\n");
                return undef;
            }
        }
        else {
            $parser->Error("_CheckRange IntegerType : INTERNAL ERROR.\n");
            return undef;
        }
    }
    elsif (  $type->isa('OctetType') ) {
        if ($value >= 0 and $value <= UCHAR_MAX) {
            return $value;
        }
        else {
            $parser->Error("'$type->{value}' $value is out of range.\n");
            return undef;
        }
    }
    elsif (  $type->isa('FloatingPtType') ) {
        return $value if ($value == 0);
        my $abs_v = abs $value;
        if (   $type->{value} eq 'float' ) {
            if ($abs_v >= FLT_MIN and $abs_v <= FLT_MAX) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' $value is out of range.\n");
                return undef;
            }
        }
        elsif ($type->{value} eq 'double') {
            if ($abs_v >= DBL_MIN and $abs_v <= DBL_MAX) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' $value is out of range.\n");
                return undef;
            }
        }
        elsif ($type->{value} eq 'long double') {
            if ($abs_v >= LDBL_MIN and $abs_v <= LDBL_MAX) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' $value is out of range.\n");
                return undef;
            }
        }
        else {
            $parser->Error("_CheckRange FloatingPtType : INTERNAL ERROR.\n");
            return undef;
        }
    }
    elsif (  $type->isa('FixedPtConstType') ) {
        return $value;
    }
    elsif (  $type->isa('StringType')
            or $type->isa('WideStringType') ) {
        if (exists $type->{max}) {
            my @lst = split //, $value;
            my $len = @lst;
            if ($len <= $type->{max}->{value}) {
                return $value;
            }
            else {
                $parser->Error("'$type->{value}' '$value' is out of range.\n");
                return undef;
            }
        }
        return $value;
    }
}

package CORBA::IDL::Constant;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $parser->YYData->{symbtab}->Insert($self);
    my $type = $self->{type};
    CORBA::IDL::TypeDeclarator->CheckDeprecated($parser, $type);
    my $defn = CORBA::IDL::TypeDeclarator->GetEffectiveType($parser, $type);
    if (defined $defn) {
        if (        ! $defn->isa('IntegerType')
                and ! $defn->isa('EnumType')
                and ! $defn->isa('OctetType')
                and ! $defn->isa('CharType')
                and ! $defn->isa('StringType')
                and ! $defn->isa('BooleanType')
                and ! $defn->isa('FloatingPtType')
                and ! $defn->isa('WideCharType')
                and ! $defn->isa('WideStringType')
                and ! $defn->isa('FixedPtConstType') ) {
            my $idf = $defn->{idf} if (exists $defn->{idf});
            $idf ||= $type->{idf} if (exists $type->{idf});
            $idf ||= $type;
            $parser->Error("'$idf' refers a bad type for constant.\n");
            return $self;
        }
    }
    else {
        $parser->Error(__PACKAGE__ . "::_Init ERROR_INTERNAL ($type).\n");
    }
    $self->configure(
            value   =>  new CORBA::IDL::Expression($parser,
                                type        =>  $defn,
                                list_expr   =>  $self->{list_expr}
                        )
    );
    $parser->YYData->{curr_node} = $self;
}

sub Lookup {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    $class = substr $class, rindex($class, ':') + 1;
    my ($parser, $name) = @_;
    my $defn = $parser->YYData->{symbtab}->Lookup($name);
    if (defined $defn) {
        if (        ! $defn->isa($class)
                and ! $defn->isa('Enum') ) {
            $parser->Error("'$name' is not a $class.\n");
        }
        return $defn->{full};
    }
    else {
        return q{};
    }
}

package CORBA::IDL::UnaryOp;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::BinaryOp;

use base qw(CORBA::IDL::Node);

#
#   3.2.5   Literals
#

package CORBA::IDL::Literal;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::IntegerLiteral;

use base qw(CORBA::IDL::Literal);

package CORBA::IDL::StringLiteral;

use base qw(CORBA::IDL::Literal);

package CORBA::IDL::WideStringLiteral;

use base qw(CORBA::IDL::Literal);

package CORBA::IDL::CharacterLiteral;

use base qw(CORBA::IDL::Literal);

package CORBA::IDL::WideCharacterLiteral;

use base qw(CORBA::IDL::Literal);

package CORBA::IDL::FixedPtLiteral;

use base qw(CORBA::IDL::Literal);

package CORBA::IDL::FloatingPtLiteral;

use base qw(CORBA::IDL::Literal);

package CORBA::IDL::BooleanLiteral;

use base qw(CORBA::IDL::Literal);

#
#   3.11    Type Declaration
#

package CORBA::IDL::TypeDeclarators;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->line_stamp($parser);
    my @list;
    foreach (@{$self->{list_expr}}) {
        my @array_size = @{$_};
        my $idf = shift @array_size;
        my $decl;
        if (@array_size) {
            $decl = new CORBA::IDL::TypeDeclarator($parser,
                    type                =>  $self->{type},
                    idf                 =>  $idf,
                    array_size          =>  \@array_size
            );
            CORBA::IDL::TypeDeclarator->CheckDeprecated($parser, $self->{type});
        }
        else {
            $decl = new CORBA::IDL::TypeDeclarator($parser,
                    type                =>  $self->{type},
                    idf                 =>  $idf
            );
        }
        push @list, $decl->{full};
    }
    $self->configure(list_decl  =>  \@list);
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    foreach (@{$self->{list_decl}}) {
        my $defn = $parser->YYData->{symbtab}->Lookup($_);
        $defn->configure(@_);
    }
    return $self;
}

package CORBA::IDL::TypeDeclarator;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->Insert($self);
    $parser->YYData->{curr_node} = $self;
    $self->{local_type} = 1 if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $self->{type}));
    $self->{deprecated} = 1 if (CORBA::IDL::TypeDeclarator->IsDeprecated($parser, $self->{type}));
}

sub Lookup {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    $class = substr $class, rindex($class, ':') + 1;
    my ($parser, $name) = @_;
    my $defn = $parser->YYData->{symbtab}->Lookup($name);
    if (defined $defn) {
        if (        ! $defn->isa($class)
                and ! $defn->isa('NativeType')
                and ! $defn->isa('_ConstructedType')
                and ! $defn->isa('_ForwardConstructedType')
                and ! $defn->isa('BaseInterface')
                and ! $defn->isa('ForwardBaseInterface') ) {
            $parser->Error("'$name' is not a type nor a value.\n");
        }
        return $defn->{full};
    }
    else {
        return q{};
    }
}

sub GetDefn {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my ($parser, $type) = @_;
    return undef unless ($type);
    if (ref $type) {
        return $type;
    }
    else {
        my $defn = $parser->YYData->{symbtab}->Lookup($type);
        return $defn;
    }
}

sub GetEffectiveType {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my ($parser, $type) = @_;
    my $defn = CORBA::IDL::TypeDeclarator->GetDefn($parser, $type);
    unless (defined $defn) {
        $parser->Error(__PACKAGE__ . "::GetEffectiveType ERROR_INTERNAL ($type).\n");
        return undef;
    }
    while (     $defn->isa('TypeDeclarator')
            and ! exists $defn->{array_size} ) {
        $defn = CORBA::IDL::TypeDeclarator->GetDefn($parser, $defn->{type});
        unless (defined $defn) {
            $parser->Error(__PACKAGE__ . "::GetEffectiveType ERROR_INTERNAL ($defn->{type}).\n");
            return undef;
        }
    }
    return $defn;
}

sub CheckDeprecated {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my ($parser, $type) = @_;
    my $defn = CORBA::IDL::TypeDeclarator->GetDefn($parser, $type);
    return unless (defined $defn);
    if (       $defn->isa('StringType')
            or $defn->isa('WideStringType') ) {
        if (exists $defn->{max}) {
            $defn->configure(deprecated =>  1);
            $parser->Deprecated("Anonymous type.\n")
                    if ($CORBA::IDL::Parser::IDL_VERSION ge '2.4');
        }
    }
    elsif   (  $defn->isa('FixedPtType') ) {
        $defn->configure(deprecated =>  1);
        $parser->Deprecated("Anonymous type.\n")
                if ($CORBA::IDL::Parser::IDL_VERSION ge '2.4');
    }
    elsif   (  $defn->isa('SequenceType') ) {
        $defn->configure(deprecated =>  1);
        $parser->Deprecated("Anonymous type.\n")
                if ($CORBA::IDL::Parser::IDL_VERSION ge '2.4');
    }
}

sub IsDeprecated {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my ($parser, $type) = @_;
    my $defn = CORBA::IDL::TypeDeclarator->GetDefn($parser, $type);
    return (exists $defn->{deprecated} ? 1 : undef);
}

sub CheckForward {
    my $proto = shift;
    my $class = ref($proto) || $proto;

    my ($parser, $type) = @_;
    my $defn = CORBA::IDL::TypeDeclarator->GetDefn($parser, $type);
    return unless (defined $defn);
    while (        $defn->isa('SequenceType')
                or $defn->isa('TypeDeclarator') ) {
        last if (exists $defn->{array_size});
        $defn = CORBA::IDL::TypeDeclarator->GetDefn($parser, $defn->{type});
        return unless (defined $defn);
    }
    if ($defn->isa('_ForwardConstructedType')) {
        $parser->Error("'$defn->{idf}' is declared, but not defined.\n");
    }
}

sub IsaLocal {
    my $proto = shift;
    my $class = ref($proto) || $proto;

    my ($parser, $type) = @_;
    return undef unless ($type);
    my $defn = CORBA::IDL::TypeDeclarator->GetDefn($parser, $type);
    return exists $defn->{local_type} if ($defn);
    $parser->Error(__PACKAGE__ . "::IsaLocal ERROR_INTERNAL ($type).\n");
    return undef;
}

package CORBA::IDL::NativeType;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->Insert($self);
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
}

#
#   3.11.1  Basic Types
#

package CORBA::IDL::BasicType;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::FloatingPtType;

use base qw(CORBA::IDL::BasicType);

package CORBA::IDL::IntegerType;

use base qw(CORBA::IDL::BasicType);

package CORBA::IDL::CharType;

use base qw(CORBA::IDL::BasicType);

package CORBA::IDL::WideCharType;

use base qw(CORBA::IDL::BasicType);

package CORBA::IDL::BooleanType;

use base qw(CORBA::IDL::BasicType);

package CORBA::IDL::OctetType;

use base qw(CORBA::IDL::BasicType);

package CORBA::IDL::AnyType;

use base qw(CORBA::IDL::BasicType);

package CORBA::IDL::ObjectType;

use base qw(CORBA::IDL::BasicType);

package CORBA::IDL::ValueBaseType;

use base qw(CORBA::IDL::BasicType);

#
#   3.11.2  Constructed Types
#

package CORBA::IDL::_ConstructedType;

use base qw(CORBA::IDL::Node);

#   3.11.2.1    Structures
#

package CORBA::IDL::StructType;

use base qw(CORBA::IDL::_ConstructedType);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->PushCurrentScope($self);
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my @list;
    foreach (@{$self->{list_expr}}) {
        foreach (@{$_->{list_member}}) {
            push @list, $_;
            $self->{deprecated} = 1 if (CORBA::IDL::TypeDeclarator->IsDeprecated($parser, $_));
        }
        $self->{local_type} = 1 if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $_->{type}));
    }
    $self->configure(list_member    =>  \@list);    # list of 'Member'
    return $self;
}

package CORBA::IDL::Members;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    CORBA::IDL::TypeDeclarator->CheckDeprecated($parser, $self->{type});
    CORBA::IDL::TypeDeclarator->CheckForward($parser, $self->{type});
    my @list;
    foreach (@{$self->{list_expr}}) {
        my $member;
        my @array_size = @{$_};
        my $idf = shift @array_size;
        if (@array_size) {
            $member = new CORBA::IDL::Member($parser,
                    props           =>  $self->{props},
                    type            =>  $self->{type},
                    idf             =>  $idf,
                    array_size      =>  \@array_size,
                    deprecated      =>  1,
            );
            $parser->Deprecated("Anonymous type (array).\n")
                    if ($CORBA::IDL::Parser::IDL_VERSION ge '2.4');
        }
        else {
            $member = new CORBA::IDL::Member($parser,
                    props           =>  $self->{props},
                    type            =>  $self->{type},
                    idf             =>  $idf,
                    deprecated      =>  CORBA::IDL::TypeDeclarator->IsDeprecated($parser, $self->{type}),
            );
        }
        push @list, $member->{full};
    }
    $self->configure(list_member    =>  \@list);
}

package CORBA::IDL::Member;                         # idf, type[, array_size]

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $parser->YYData->{symbtab}->Insert($self);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{curr_node} = $self;
}

#   3.11.2.2    Discriminated Unions
#

package CORBA::IDL::UnionType;

use base qw(CORBA::IDL::_ConstructedType);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->PushCurrentScope($self);
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my $dis = $self->{type};
    my $defn = CORBA::IDL::TypeDeclarator->GetEffectiveType($parser, $dis);
    if (defined $defn) {
        if (        ! $defn->isa('IntegerType')
                and ! $defn->isa('CharType')
                and ! $defn->isa('BooleanType')
                and ! $defn->isa('EnumType') ) {
            my $idf = $defn->{idf} if (exists $defn->{idf});
            $idf ||= $dis->{idf} if (exists $dis->{idf});
            $idf ||= $dis;
            $parser->Error("'$idf' refers a bad type for union discriminator.\n");
            return $self;
        }
    }
    my %hash;
    my @list_all;
    foreach my $case (@{$self->{list_expr}}) {
        my $elt = $case->{element};
        $self->{local_type} = 1 if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $elt->{type}));
        $self->{deprecated} = 1 if (CORBA::IDL::TypeDeclarator->IsDeprecated($parser, $elt->{value}));
        my @list;
        foreach (@{$case->{list_label}}) {
            my $key;
            if (ref $_ eq 'CORBA::IDL::Default') {
                $key = 'Default';
                push @list, $_;
                $self->configure(default    =>  $case);
            }
            else {
                # now, type is known
                my $cst = new CORBA::IDL::Expression($parser,
                        type                =>  $dis,
                        list_expr           =>  $_
                );
                if ($defn->isa('EnumType')) {
                    $key = $cst->{value}->{full};
                }
                else {
                    $key = $cst->{value};
                }
                push @list, $cst;
                push @list_all, $cst;
            }
            if (defined $key) {
                if (exists $hash{$key}) {
                    $parser->Error("label value '$key' is duplicate for union.\n");
                }
                else {
                    $hash{$key} = $elt;
                }
            }
        }
        $case->{list_label} = \@list;
    }
    $self->configure(list_member    =>  \@list_all);
    $self->configure(hash_member    =>  \%hash);
    if ($defn->isa('EnumType')) {
        my $all = 1;
        foreach (@{$defn->{list_member}}) {
            $all = 0 unless (exists $hash{$_});
        }
        if ($all) {
            $parser->Error("illegal label 'default'.\n")
                    if (exists $self->{default});
        }
        else {
            $self->configure(need_default   =>  1)
                    unless (exists $self->{default});
        }
    }
    else {
        $self->configure(need_default   =>  1)
                unless (exists $self->{default});
    }
    return $self;
}

package CORBA::IDL::Case;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::Default;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::Element;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    CORBA::IDL::TypeDeclarator->CheckDeprecated($parser, $self->{type});
    CORBA::IDL::TypeDeclarator->CheckForward($parser, $self->{type});
    my @array_size = @{$self->{list_expr}};
    my $idf = shift @array_size;
    my $value;
    if (@array_size) {
        $value = new CORBA::IDL::Member($parser,
                type            =>  $self->{type},
                idf             =>  $idf,
                array_size      =>  \@array_size,
                deprecated      =>  1,
        );
        $parser->Deprecated("Anonymous type (array).\n")
                if ($CORBA::IDL::Parser::IDL_VERSION ge '2.4');
    }
    else {
        $value = new CORBA::IDL::Member($parser,
                type            =>  $self->{type},
                idf             =>  $idf,
                deprecated      =>  CORBA::IDL::TypeDeclarator->IsDeprecated($parser, $self->{type}),
        );
    }
    $self->configure(value  =>  $value->{full});    # 'Member'
}

#   3.11.2.3    Constructed Recursive Types and Forward Declarations
#

package CORBA::IDL::_ForwardConstructedType;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    $parser->YYData->{symbtab}->InsertForward($self);
    $parser->Error("Forward constructed not supported.\n")
            if ($parser->YYData->{forward_constructed_forbidden});

}

package CORBA::IDL::ForwardStructType;

use base qw(CORBA::IDL::_ForwardConstructedType);

package CORBA::IDL::ForwardUnionType;

use base qw(CORBA::IDL::_ForwardConstructedType);

#   3.11.2.4    Enumerations
#

package CORBA::IDL::EnumType;

use base qw(CORBA::IDL::_ConstructedType);

use constant ULONG_MAX      => 4294967295;

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->Insert($self);
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my $idx = 0;                        # Section 15.3 CDR Transfer Syntax
                                        # 15.3.2.6 Enum
    my %hash;
    my @list;
    foreach (@{$self->{list_expr}}) {
        if (exists $hash{$_->{idf}}) {
            $parser->Error("enum '$_->{idf}' is duplicate.\n");
        }
        else {
            $hash{$_->{idf}} = $idx;
            push @list, $_->{full};
        }
        $_->configure(
                type        =>  $self->{full},
                value       =>  "$idx"
        );
        $idx++;
    }
    $self->configure(list_member    =>  \@list);    # list of 'Enum'    #### ????
    if ($idx > ULONG_MAX) {
        $parser->Error("too many enum for '$self->{idf}'.\n");
    }
    return $self;
}

package CORBA::IDL::Enum;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->Insert($self);
    $parser->YYData->{curr_node} = $self;
}

#
#   3.11.3  Template Types
#

package CORBA::IDL::_TemplateType;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::SequenceType;

use base qw(CORBA::IDL::_TemplateType);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->line_stamp($parser);
    $parser->YYData->{symbtab}->InsertBogus($self);
    CORBA::IDL::TypeDeclarator->CheckDeprecated($parser, $self->{type});
    $self->{local_type} = 1 if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $self->{type}));
}

package CORBA::IDL::StringType;

use base qw(CORBA::IDL::_TemplateType);

package CORBA::IDL::WideStringType;

use base qw(CORBA::IDL::_TemplateType);

package CORBA::IDL::FixedPtType;

use base qw(CORBA::IDL::_TemplateType);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->line_stamp($parser);
}

package CORBA::IDL::FixedPtConstType;

use base qw(CORBA::IDL::_TemplateType);

#
#   3.12    Exception Declaration
#

package CORBA::IDL::Exception;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->{prefix} = $parser->YYData->{symbtab}->GetPrefix();
    $self->{_typeprefix} = $parser->YYData->{symbtab}->GetTypePrefix();
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->PushCurrentScope($self);
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my @list;
    foreach (@{$self->{list_expr}}) {
        foreach (@{$_->{list_member}}) {
            push @list, $_;
        }
        $self->{local_type} = 1 if (CORBA::IDL::TypeDeclarator->IsaLocal($parser, $_->{type}));
    }
    $self->configure(list_member    =>  \@list);    # list of 'Member'
    return $self;
}

sub Lookup {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    $class = substr $class, rindex($class, ':') + 1;
    my ($parser, $name) = @_;
    my $defn = $parser->YYData->{symbtab}->Lookup($name);
    if (defined $defn) {
        unless ($defn->isa($class) || $defn->isa('NativeType')) {
            $parser->Error("'$name' is not a $class or a native type.\n");
        }
        return $defn->{full};
    }
    else {
        return q{};
    }
}

#
#   3.13    Operation Declaration
#

package CORBA::IDL::Operation;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    my $type = $self->{type};
    $self->line_stamp($parser);
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{symbtab}->Insert($self);
    $parser->YYData->{unnamed_symbtab} = new CORBA::IDL::UnnamedSymbtab($parser);
    CORBA::IDL::TypeDeclarator->CheckDeprecated($parser, $type);
    CORBA::IDL::TypeDeclarator->CheckForward($parser, $type);
    if (defined $parser->YYData->{curr_itf}) {
        $self->{itf} = $parser->YYData->{curr_itf}->{full};
        $parser->YYData->{curr_itf}->{hash_attribute_operation}{$self->{idf}} = $self->{full}
                unless($self->{idf} =~ /^_/);       # _get_ or _set_
    }
    else {
        $parser->Error(__PACKAGE__ . "::new ERROR_INTERNAL.\n");
    }
    unless (ref $type) {
        if ($type =~ /::([0-9A-Z_a-z]+)$/) {
            $parser->YYData->{unnamed_symbtab}->InsertUsed($1);
        }
    }
    $parser->YYData->{curr_node} = $self;
}

sub _CheckOneway {
    my $self = shift;
    my ($parser) = @_;
    if (exists $self->{modifier} and $self->{modifier} eq 'oneway') {
        # 3.12.1    Operation Attribute
        my $type = $self->{type};
        unless (ref $type or $type->isa('VoidType')) {
            $parser->Error("return type of '$self->{idf}' is not 'void'.\n");
        }
        foreach ( @{$self->{list_param}} ) {
            next if ($_->isa('Ellipsis'));
            if ($_->{attr} ne 'in') {
                $parser->Error("parameter '$_->{idf}' is not 'in'.\n");
            }
        }
        if (exists $self->{list_raise}) {
            $parser->Error("oneway operation can't raise exception.\n");
        }
    }
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    $self->_CheckOneway($parser);
    my @list_in = ();
    my @list_inout = ();
    my @list_out = ();
    foreach ( @{$self->{list_param}} ) {
        next if ($_->isa('Ellipsis'));
        if    ($_->{attr} eq 'in') {
            push @list_in, $_;
        }
        elsif ($_->{attr} eq 'inout') {
            push @list_inout, $_;
        }
        elsif ($_->{attr} eq 'out') {
            push @list_out, $_;
        }
    }
    $self->{list_in} = \@list_in;
    $self->{list_inout} = \@list_inout;
    $self->{list_out} = \@list_out;
    return $self;
}

package CORBA::IDL::Parameter;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->line_stamp($parser);
    my $type = $self->{type};
    unless (ref $type) {
        if ($type =~ /::([0-9A-Z_a-z]+)$/) {
            $parser->YYData->{unnamed_symbtab}->InsertUsed($1);
        }
    }
    CORBA::IDL::TypeDeclarator->CheckDeprecated($parser, $type);
    CORBA::IDL::TypeDeclarator->CheckForward($parser, $type);
    $parser->YYData->{unnamed_symbtab}->Insert($self->{idf});
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{curr_node} = $self;
}

package CORBA::IDL::VoidType;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::Ellipsis;

use base qw(CORBA::IDL::Node);

#
#   3.14    Attribute Declaration
#

package CORBA::IDL::Attributes;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    my @list;
    foreach (@{$self->{list_expr}}) {
        my $attr = new CORBA::IDL::Attribute($parser,
                declspec            =>  $self->{declspec},
                props               =>  $self->{props},
                modifier            =>  $self->{modifier},
                type                =>  $self->{type},
                idf                 =>  $_,
                list_getraise       =>  $self->{list_getraise},
                list_setraise       =>  $self->{list_setraise}
        );
        push @list, $attr->{full};
    }
    $self->configure(list_decl  =>  \@list);
}

package CORBA::IDL::Attribute;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    return unless ($self->{idf});
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $self->line_stamp($parser);
    $parser->YYData->{symbtab}->Insert($self);
    if (defined $parser->YYData->{curr_itf}) {
        $self->{itf} = $parser->YYData->{curr_itf}->{full};
        $parser->YYData->{curr_itf}->{hash_attribute_operation}{$self->{idf}} = $self->{full};
    }
    else {
        $parser->Error(__PACKAGE__ . "::new ERROR_INTERNAL.\n");
    }
    $parser->YYData->{curr_node} = $self;
    my $op = new CORBA::IDL::Operation($parser,
            type                =>  $self->{type},
            idf                 =>  '_get_' . $self->{idf}
    );
    $op->Configure($parser,
            list_param      =>  [],
            list_raise      =>  $self->{list_getraise}
    );
    $self->configure(
            _get        =>  $op
    );
    unless (exists $self->{modifier}) {     # readonly
        $op = new CORBA::IDL::Operation($parser,
                type            =>  new CORBA::IDL::VoidType($parser,
                                            value       =>  'void'
                                    ),
                idf             =>  '_set_' . $self->{idf}
        );
        # unnamed_symbtab created
        $op->Configure($parser,
                list_param      =>  [
                                        new CORBA::IDL::Parameter($parser,
                                                attr    =>  'in',
                                                type    =>  $self->{type},
                                                idf     =>  'new' . ucfirst $self->{idf}
                                        )
                                    ],
                list_raise      =>  $self->{list_setraise}
        );
        $self->configure(
                _set        =>  $op
        );
    }
}

#
#   3.15    Repository Identity Related Declarations
#

package CORBA::IDL::TypeId;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    my $node = $parser->YYData->{symbtab}->Lookup($self->{idf});
    if (defined $node) {
        if (       $node->isa('Modules')
                or $node->isa('BaseInterface')
                or $node->isa('ForwardBaseInterface')
                or $node->isa('StateMember')
                or $node->isa('Constant')
                or $node->isa('TypeDeclarator')
                or $node->isa('Enum')
                or $node->isa('Exception')
                or $node->isa('Operation')
                or $node->isa('Attribute')
                or $node->isa('Provides')
                or $node->isa('Uses')
                or $node->isa('Emits')
                or $node->isa('Publishes')
                or $node->isa('Consumes')
                or $node->isa('Factory')
                or $node->isa('Finder') ) {
            if (exists $node->{id}) {
                $parser->Warning("TypeId/pragma conflict for '$self->{idf}'.\n");
            }
            if (exists $node->{typeid}) {
                $parser->Error("TypeId redefinition for '$self->{idf}'.\n");
            }
            else {
                $parser->YYData->{symbtab}->CheckID($node, $self->{value});
                $node->{typeid} = $self->{value};
            }
        }
        else {
            $parser->Error("Typeid not allowed for '$self->{idf}'.\n");
        }
    }
}

package CORBA::IDL::TypePrefix;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    unless ($self->{value} =~ /^[0-9A-Za-z_:\.\/\-]*$/) {
        $parser->Warning("Invalid TypePrefix format for \"$self->{value}\".\n");
    }
    if ($self->{idf}) {
        my $node = $parser->YYData->{symbtab}->Lookup($self->{idf});
        if (defined $node) {
            if (       $node->isa('Modules')
                    or $node->isa('Interface')
                    or $node->isa('ForwardInterface')
                    or $node->isa('Value')
                    or $node->isa('ForwardValue')
                    or $node->isa('Specification') ) {
                if ($node->{prefix}) {
                    $parser->Warning("TypePrefix/pragma conflict for '$self->{idf}'.\n");
                }
                $node->{typeprefix} = $self->{value};
                $node->{_typeprefix} = $self->{value};
                $parser->YYData->{symbtab}->{typeprefix}->{$node->{full}} = $self->{value} . '/' . $node->{idf};
            }
            else {
                $parser->Error("Typeprefix not allowed for '$self->{idf}'.\n");
            }
        }
    }
    else {
        $parser->YYData->{symbtab}->{typeprefix}->{''} = $self->{value};
    }
}

#
#   3.16    Event Declaration
#

package CORBA::IDL::Event;

use base qw(CORBA::IDL::Value);

package CORBA::IDL::RegularEvent;

use base qw(CORBA::IDL::Event);

sub _CheckInheritance {
    my $self = shift;
    my ($parser) = @_;
    if (exists $self->{inheritance}) {
        if (    exists $self->{inheritance}->{modifier}     # truncatable
            and exists $self->{modifier} ) {                # custom
            $parser->Error("'truncatable' is used in a custom event.\n");
        }
    }
}

sub _CheckLocal {
    # A local type may be used as a parameter, attribute, return type, or exception
    # declaration of a local interface or of a valuetype.
}

package CORBA::IDL::AbstractEvent;

use base qw(CORBA::IDL::Event);

sub _CheckInheritance {
    # empty
}

sub _CheckLocal {
    # A local type may be used as a parameter, attribute, return type, or exception
    # declaration of a local interface or of a valuetype.
}

package CORBA::IDL::ForwardEvent;

use base qw(CORBA::IDL::ForwardValue);

package CORBA::IDL::ForwardRegularEvent;

use base qw(CORBA::IDL::ForwardEvent);

package CORBA::IDL::ForwardAbstractEvent;

use base qw(CORBA::IDL::ForwardEvent);

#
#   3.17    Component Declaration
#

package CORBA::IDL::Component;

use base qw(CORBA::IDL::BaseInterface);

sub _CheckInheritance {
}

package CORBA::IDL::ForwardComponent;

use base qw(CORBA::IDL::ForwardBaseInterface);

package CORBA::IDL::Provides;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::Uses;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::Emits;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::Publishes;

use base qw(CORBA::IDL::Node);

package CORBA::IDL::Consumes;

use base qw(CORBA::IDL::Node);

#
#   3.18    Home Declaration
#

package CORBA::IDL::Home;

use base qw(CORBA::IDL::BaseInterface);

sub _CheckInheritance {
}

package CORBA::IDL::Factory;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $parser->YYData->{symbtab}->Insert($self);
    $parser->YYData->{unnamed_symbtab} = new CORBA::IDL::UnnamedSymbtab($parser);
    if (defined $parser->YYData->{curr_itf}) {
        $self->{itf} = $parser->YYData->{curr_itf}->{full};
        $parser->YYData->{curr_itf}->{hash_attribute_operation}{$self->{idf}} = $self->{full}
    }
    else {
        $parser->Error(__PACKAGE__ . "::new ERROR_INTERNAL.\n");
    }
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my @list_in = ();
    foreach ( @{$self->{list_param}} ) {
        if      ($_->{attr} eq 'in') {
            unshift @list_in, $_;
        }
    }
    $self->{list_in} = \@list_in;
    $self->{list_inout} = [];
    $self->{list_out} = [];
    return $self;
}

package CORBA::IDL::Finder;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $parser->YYData->{symbtab}->Insert($self);
    $parser->YYData->{unnamed_symbtab} = new CORBA::IDL::UnnamedSymbtab($parser);
    if (defined $parser->YYData->{curr_itf}) {
        $self->{itf} = $parser->YYData->{curr_itf}->{full};
        $parser->YYData->{curr_itf}->{hash_attribute_operation}{$self->{idf}} = $self->{full}
    }
    else {
        $parser->Error(__PACKAGE__,"::new ERROR_INTERNAL.\n");
    }
    if ($parser->YYData->{doc} ne q{}) {
        $self->{doc} = $parser->YYData->{doc};
        $parser->YYData->{doc} = q{};
    }
    $parser->YYData->{curr_node} = $self;
}

sub Configure {
    my $self = shift;
    my $parser = shift;
    $self->configure(@_);
    my @list_in = ();
    foreach ( @{$self->{list_param}} ) {
        if      ($_->{attr} eq 'in') {
            unshift @list_in, $_;
        }
    }
    $self->{list_in} = \@list_in;
    $self->{list_inout} = [];
    $self->{list_out} = [];
    return $self;
}

package CORBA::IDL::CodeFragment;

use base qw(CORBA::IDL::Node);

sub _Init {
    my $self = shift;
    my ($parser) = @_;
    $self->line_stamp($parser);
}

=for tree

    Node
        Specification -
        Import -
        Modules - NEW
        Module
        (BaseInterface) -
            (Interface)
                RegularInterface
                LocalInterface
                AbstractInterface
            (Value)
                RegularValue
                BoxedValue
                AbstractValue
                (Event) -
                    RegularEvent
                    AbstractEvent
            Component
            Home
        (ForwardBaseInterface)
            (ForwardInterface) -
                ForwardRegularInterface
                ForwardLocalInterface
                ForwardAbstractInterface
            (ForwardValue) -
                ForwardRegularValue -
                ForwardAbstractValue -
                (ForwardEvent) -
                    ForwardRegularEvent -
                    ForwardAbstractEvent -
            ForwardComponent -
        InheritanceSpec
        StateMembers
        StateMember
        Initializer
        Expression
        Constant
        UnaryOp -
        BinaryOp -
        (Literal)
            IntegerLiteral -
            StringLiteral -
            WideStringLiteral -
            CharacterLiteral -
            WideCharacterLiteral -
            FixedPtLiteral -
            FloatingLiteral -
            BooleanLiteral -
        TypeDeclarator
        TypeDeclarators
        NativeType
        (BasicType)
            FloatingPtType -
            IntegerType -
            CharType -
            WideCharType -
            BooleanType -
            OctetType -
            AnyType -
            ObjectType -
            ValueBaseType -
        (_ConstructedType)
            StructType
            UnionType
            EnumType
        (_ForwardConstructedType)
            ForwardStructType -
            ForwardUnionType -
        Members
        Member
        Case -
        Default -
        Element
        Enum
        (_TemplateType) -
            SequenceType
            StringType -
            WideStringType -
            FixedPtType
            FixedPtConstType - NEW
        Exception
        Operation
        Parameter
        VoidType -
        Ellipsis -
        Attributes
        Attribute
        TypeId
        TypePrefix
        Provides
        Uses
        Emits
        Publishes
        Consumes
        Factory
        Finder

=end tree

1;