package Anarres::Mud::Driver::Compiler::Type;
use strict;
use warnings;
use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS
%TYPENAMES %TYPECODES);
use Exporter;
use Carp;
BEGIN {
$VERSION = 0.10;
@ISA = qw(DynaLoader Exporter);
@EXPORT_OK = qw(T_CLASS F_CONST F_LVALUE); # .xs adds more to this
%EXPORT_TAGS = (
all => \@EXPORT_OK,
);
require DynaLoader;
bootstrap Anarres::Mud::Driver::Compiler::Type;
}
%TYPENAMES = (
${T_VOID()} => "void",
${T_NIL()} => "nil",
${T_UNKNOWN()} => "unknown",
${T_BOOL()} => "boolean",
${T_CLOSURE()} => "function",
${T_INTEGER()} => "integer",
${T_OBJECT()} => "object",
${T_STRING()} => "string",
${T_FAILED()} => "ERROR",
);
%TYPECODES = (
${T_VOID()} => "void",
${T_NIL()} => "nil",
${T_UNKNOWN()} => "mixed",
${T_BOOL()} => "bool",
${T_CLOSURE()} => "function",
${T_INTEGER()} => "int",
${T_OBJECT()} => "object",
${T_STRING()} => "string",
${T_FAILED()} => "ERROR",
);
sub T_CLASS {
my $class = __PACKAGE__;
my $name = shift;
# DEBUG
croak "Error: Class must be named." if ref($name);
my $self = T_M_CLASS_BEGIN . $name . T_M_CLASS_MID .
join('', map { $$_ } @_) . T_M_CLASS_END;
return $class->new($self);
}
sub F_CONST () { 1 }
sub F_LVALUE () { 2 }
sub array {
my ($self, $num) = @_;
$num = 1 unless defined $num;
my $out = "*" x $num . $$self;
return $self->new($out);
}
sub mapping {
my ($self, $num) = @_;
$num = 1 unless defined $num;
my $out = "#" x $num . $$self;
return $self->new($out);
}
sub dereference {
my ($self) = @_;
my $new;
if ($$self =~ /^[*#]/) {
$new = substr($$self, 1)
}
elsif ($$self eq ${ &T_STRING }) { # XXX Remove this case?
warn "Dereferencing string!";
$new = T_INTEGER;
}
else {
die "Cannot dereference nonreference type $$self";
}
return $self->new($new);
}
sub is_array {
return ${$_[0]} =~ /^\*/;
}
sub is_mapping {
return ${$_[0]} =~ /^#/;
}
sub is_class {
return ${$_[0]} =~ /^{/;
}
sub class {
return undef unless ${$_[0]} =~ /^{([^:]*):/;
return $1;
}
sub dump {
return ${$_[0]};
}
sub equals {
# Since we have unique types, the references should compare
# equal just as the referenced values do.
warn "Problem with type uniqueness"
if (($_[0] == $_[1]) != (${$_[0]} eq ${$_[1]}));
return ${$_[0]} eq ${$_[1]};
}
# Called from Node->promote in Check.pm
sub promote {
my ($self, $node, $type) = @_;
# We might be promoted to a more specific type.
# We might be promoted to a less specific type.
# This routine must return a typechecked object.
if ($$self ne $$type) {
# print "Promoting " . sprintf("%-20.20s", $node->nodetype) .
# " from $$self to $$type\n";
}
return $node; # XXX do something here!
}
sub name {
my ($self) = shift;
my $code = $$self;
my $out = "";
while (length $code) {
if ($code =~ s/^#//) {
$out .= "mapping of ";
}
elsif ($code =~ s/^\*//) {
$out .= "pointer to ";
}
elsif ($code =~ m/^z/) {
$out .= "constant ";
}
elsif ($code =~ m/^=/) {
$out .= "lvalue ";
}
elsif ($code =~ m/^{([^:]+):/) {
return $out . "class $1";
}
elsif ($TYPENAMES{$code}) {
return $out . $TYPENAMES{$code};
}
else {
die "Unknown type code $code!";
}
}
die "Invalid type code $$self !";
}
# Currently only called from Method::proto
sub deparse {
my ($self) = shift;
my $code = $$self;
my $out = "";
while (length $code) {
if ($code =~ s/^#//) {
$out .= "#";
}
elsif ($code =~ s/^\*//) {
$out .= "*";
}
elsif ($code =~ m/^z/) {
# $out .= "const ";
}
elsif ($code =~ m/^=/) {
# $out .= "lvalue ";
}
elsif ($code =~ m/^{([^:]+):/) {
return "class $1 $out";
}
elsif ($TYPECODES{$code}) {
return "$TYPECODES{$code} $out" if length $out;
return $TYPECODES{$code};
}
else {
die "Unknown type code $code!";
}
}
die "Invalid type code $$self !";
}
1;