use v5.10;
use Mildew::Emit::Yeast;
use MooseX::Declare;
class Mildew::TypeInfo {
has type=>(is=>'rw',builder=>'infer_type',lazy=>1);
has mold=>(is=>'rw',isa=>'Mildew::AST::Block::SSA');
has usage=>(is=>'ro',isa=>'ArrayRef[Mildew::AST::Base]',default=>sub {[]});
method add_usage($stmt) {
push(@{$self->usage},$stmt);
}
method infer_type {
Mildew::Type::Unknown->new();
}
}
class Mildew::TypeInfo::FromAssignment extends Mildew::TypeInfo {
has orgin=>(is=>'ro',isa=>'Mildew::AST::Base',required=>1);
method infer_type {
$self->type(Mildew::Type::SelfRecursive->new());
my $rvalue = $self->orgin->rvalue;
if ($rvalue->isa('Mildew::AST::Call')) {
my $type = $rvalue->capture->invocant->type_info->type->method_call($self->orgin);
$self->type($type);
for my $usage (@{$self->usage}) {
$type->add_usage($self->orgin->lvalue,$usage);
}
$type;
} else {
Mildew::Type::Unknown->new();
}
}
}
class Mildew::TypeInfo::Phi extends Mildew::TypeInfo {
}
class Mildew::TypeInfo::IntegerConstant extends Mildew::TypeInfo {
method infer_type {
Mildew::Type::IntegerConstant->new();
}
}
class Mildew::TypeInfo::StringConstant extends Mildew::TypeInfo {
}
class Mildew::TypeInfo::External extends Mildew::TypeInfo {
}
class Mildew::Type {
sub str {
my ($value,$str) = @_;
is_str($value) && $value->value;
}
sub is_str {
my ($value) = @_;
defined $value && $value->isa('Mildew::AST::StringConstant');
}
method debug {
}
method method_call($call) {
Mildew::Type::Unknown->new();
}
method emit_call($i,$stmt,$value) {
my $list = sub {
"(SMOP__Object*[]) {" . join(',',(map {"SMOP_REFERENCE(interpreter,".$value->($_).")"} @_),"NULL") . "}"
};
my $capture = $stmt->rvalue->capture;
Mildew::Emit::Yeast::measure($stmt->id,"frame->pc = " . ($i+1) . ";\n"
. "frame->ret = &" . $value->($stmt->lvalue) . ";\n"
. Mildew::Emit::Yeast::assign($value->($stmt->lvalue),
"SMOP_DISPATCH(\n" . "interpreter,\nSMOP_RI(" . $value->($capture->invocant) . "),\n"
. $value->($stmt->rvalue->identifier)
. ",\nSMOP__NATIVE__capture_create(interpreter,"
. $list->($capture->invocant,@{$capture->positional})
. ","
. $list->(@{$capture->named})
. ")\n" . ")")
. "break;\n");
}
method emit_perlesque_call($stmt,$value) {
my $list = sub {
"[" . join(',',(map {$value->($_)} @_)) . "]"
};
my $capture = $stmt->rvalue->capture;
my $perlesque_capture = Mildew::AST::unique_id();
my $add_named = '';
my @named = @{$capture->named};
use Devel::PartialDump qw(warn);
while (@named) {
warn("named:",\@named);
$add_named .= "$perlesque_capture.add_named(" . $value->(shift @named) . "," .$value->(shift @named) . ")";
}
"my P6capture $perlesque_capture = P6capture.new();\n"
. $add_named
. join("",(map {"$perlesque_capture.add_positional(" . $value->($_) . ");\n"} ($capture->invocant,@{$capture->positional})))
. $value->($stmt->lvalue) . " = "
. $value->($capture->invocant)
. ".DISPATCH(" . $value->($stmt->rvalue->identifier)
. ",$perlesque_capture"
#. ",p6capture("
#. $list->($capture->invocant,@{$capture->positional})
#. ','
#. $list->(@{$capture->named})
. ");"
}
method add_usage($reg,$stmt) {
}
method pretty() {
ref $self;
}
}
role Mildew::Type::FETCH {
method method_call($stmt) {
my $call = $stmt->rvalue;
my $id = Mildew::Type::str($call->identifier);
if ($id eq 'FETCH') {
$self;
} else {
Mildew::Type::Unknown->new();
}
}
}
class Mildew::Type::IntegerConstant extends Mildew::Type with Mildew::Type::FETCH {
}
class Mildew::Type::Prototype extends Mildew::Type {
has type=>(is=>'ro');
method method_call($stmt) {
my $call = $stmt->rvalue;
my $id = Mildew::Type::str($call->identifier);
if ($id eq 'new') {
$self->type->();
} elsif ($id eq 'FETCH') {
$self;
}
}
}
class Mildew::Type::Scope extends Mildew::Type {
has content=>(is=>'rw');
has reg=>(is=>'rw',isa=>'Mildew::AST::Reg');
has outer=>(is=>'ro',isa=>'Mildew::Type');
use Scalar::Util qw(refaddr);
use Term::ANSIColor qw(:constants);
# XXX do more genericly with add_usage
method infer_lexicals {
$self->debug("infering lexicals");
#return undef unless $self->reg;
$self->content({});
for my $stmt (@{$self->reg->type_info->usage}) {
if ($stmt->rvalue->isa('Mildew::AST::Call')) {
my $call = $stmt->rvalue;
if (refaddr($call->capture->invocant) == refaddr($self->reg)) {
if (
@{$call->capture->positional} == 1
&& Mildew::Type::is_str($call->capture->positional->[0])
) {
my $name = Mildew::Type::str($call->capture->positional->[0]);
if (Mildew::Type::str($call->identifier) eq 'postcircumfix:{ }') {
$self->debug("variable defined in scope: ",GREEN,$name,RESET);
$self->content->{$name} = Mildew::Type::Lexical->new();
$stmt->lvalue->type_info->type();
} elsif (Mildew::Type::str($call->identifier) eq 'lookup') {
$self->debug("variable used in scope:",GREEN,$name,RESET);
$stmt->lvalue->type_info->type();
} else {
$self->debug("unknown usage of scope:",RED,$stmt->pretty,RESET);
}
} else {
$self->debug("unknown usage of scope:",$stmt->pretty);
}
}
}
}
$self->debug("infered lexicals");
}
method lookup($varname) {
$self->debug("looking up $varname");
if (!defined $self->content) {
return Mildew::Type::Unknown->new();
}
if (my $type = $self->content->{$varname}) {
$type;
} elsif ($self->outer) {
$self->outer->lookup($varname);
} else {
Mildew::Type::Unknown->new();
}
}
method method_call($stmt) {
my $call = $stmt->rvalue;
$self->reg($call->capture->invocant);
if (!defined $self->content) {
$self->infer_lexicals;
}
my $id = Mildew::Type::str($call->identifier);
if (($id eq 'lookup' || $id eq 'postcircumfix:{ }') && Mildew::Type::is_str($call->capture->positional->[0])) {
$self->debug("handling method:",$id);
$self->lookup(Mildew::Type::str($call->capture->positional->[0]));
} else {
$self->debug("not handling method:",$id);
Mildew::Type::Unknown->new();
}
}
}
class Mildew::Type::Lexical extends Mildew::Type {
use Term::ANSIColor qw(:constants);
use Scalar::Util qw(refaddr);
use Carp qw(cluck);
has content=>(is=>'rw',isa=>'Mildew::Type',lazy_build=>1);
has binds=>(is=>'ro',isa=>'ArrayRef[Type]',default=>sub {[]});
has stores=>(is=>'ro',isa=>'ArrayRef[Type]',default=>sub {[]});
method _build_content {
$self->debug("infering content of lexical");
my $container;
if (@{$self->binds} == 1) {
$container = $self->binds->[0];
$self->debug("1 BIND: ",$self->binds->[0]);
} else {
$self->debug("many BINDs");
$container = Mildew::Type::Scalar->new();
}
use Data::Dumper;
$self->debug('stores = ',Dumper($self->stores));
if ($container->can('add_store')) {
$container->add_store($_) for @{$self->stores};
}
$self->debug("infered content of lexical");
$container;
}
method method_call($call) {
if (Mildew::Type::str($call->rvalue->identifier) eq 'FETCH') {
$self->debug(RED,"called FETCH on lexical",RESET);
$self->content->method_call($call);
} elsif (Mildew::Type::str($call->rvalue->identifier) eq 'BIND') {
$self;
} else {
Mildew::Type::Unknown->new();
}
}
method add_usage($reg,$usage) {
if ($usage->isa('Mildew::AST::Assign')) {
my $call = $usage->rvalue;
if ($call->isa('Mildew::AST::Call') && (refaddr $call->capture->invocant == refaddr $reg)) {
my $id = Mildew::Type::str($call->identifier);
if ($id eq 'BIND') {
push (@{$self->binds},$call->capture->positional->[0]->type_info->type);
$self->debug("propagating {");
$usage->lvalue->type_info->type();
$self->debug("}");
return;
} elsif ($id eq 'STORE') {
$self->debug("STORE on lexicals {");
push (@{$self->stores},$call->capture->positional->[0]->type_info->type);
$self->debug("}");
return;
} elsif ($id eq 'FETCH') {
return;
}
}
}
$self->debug(RED,"unknow usage of lexical ",$reg->pretty,": ",$usage->pretty,RESET);
}
method pretty {
(ref $self) . " of " . $self->content->pretty;
}
}
class Mildew::Type::Scalar extends Mildew::Type {
use Carp qw(cluck);
use Scalar::Util qw(refaddr);
has stores=>(is=>'ro',isa=>'ArrayRef[Type]',default=>sub {[]});
has content=>(is=>'rw',builder=>'infer_content',lazy=>1,predicate=>'has_content');
method add_store($content) {
$self->debug("adding store to ",(refaddr $self));
push(@{$self->stores},$content);
}
method infer_content {
if (@{$self->stores} == 1) {
$self->debug("just enough stores");
$self->stores->[0];
} else {
use Data::Dumper;
#cluck "wrong number of stores: ",(refaddr $self),Dumper($self->stores);
Mildew::Type::Unknown->new();
}
}
method method_call($call) {
if (Mildew::Type::str($call->rvalue->identifier) eq 'FETCH') {
$self->content;
} else {
Mildew::Type::Unknown->new();
}
}
method pretty {
'Mildew::Type::Scalar of ' . ($self->has_content ? $self->content->pretty : '(not calculated yet value)');
}
}
class Mildew::Type::Unknown extends Mildew::Type {
}
class Mildew::Type::SelfRecursive extends Mildew::Type::Unknown {
method method_call($call) {
$self->debug("method call on self recursive\n");
Mildew::Type::SelfRecursive->new();
}
}
class Mildew::Type::MildewSOLoader extends Mildew::Type {
}
$Mildew::LexicalPreludeType = Mildew::Type::Scope->new(
content => {
MildewSOLoader => Mildew::Type::Lexical->new(content=>Mildew::Type::MildewSOLoader->new()),
Scalar => Mildew::Type::Prototype->new(type=>sub {Mildew::Type::Scalar->new()}),
}
);