my
@perl_builtin_undefined
=
qw(
Mu
Failure
Any
Cool
)
;
my
@perl_builtin_immutable
=
qw(
Str
!Bit
Int
Num
Rat
!FatRat
Complex
Bool
Exception
Block
Seq
Range
Set
!Bag
Enum
EnumMap
Signature
Parcel
!Slicel
Capture
!Blob
!Instant
!Duration
!HardRoutine
)
;
my
@perl_builtin_mutable
=
qw(
Iterator
!SeqIter
RangeIter
!Scalar
Array
Hash
!KeySet
!KeyBag
!KeyHash
Pair
!PairSeq
Buf
IO
Routine
Sub
Method
Submethod
!Macro
Regex
Match
!Stash
!SoftRoutine
DateTime
)
;
my
@perl_builtin_roles
=
qw(
Iterable
)
;
my
$perl_builtin_types
= {
map
{ (/^!/) ? () : (
$_
=>1) } (
@perl_builtin_undefined
,
@perl_builtin_immutable
,
@perl_builtin_mutable
,
@perl_builtin_roles
,
) };
if
(0) {
for
my
$t
(
keys
%$perl_builtin_types
) {
system
(
"perl6 -e 'my $t \$v'"
);
}
}
sub
perl6_string_literal_filter {
my
$value
=
shift
;
$value
=~ s/([^[:
print
:]])/
sprintf
"\\u%04x"
,
ord
($1) /eg;
return
qq{"$value"}
;
}
sub
new {
my
$class
=
shift
;
my
$self
=
bless
{
@_
},
$class
;
my
$filters
= Template::Filters->new({
FILTERS
=> {
perl6_string_literal
=> \
&perl6_string_literal_filter
,
},
});
my
$tt_args
= {
POST_CHOMP
=> 1,
LOAD_FILTERS
=> [
$filters
],
};
$self
->tt_args_set(
$tt_args
);
return
$self
;
}
sub
tt_args_set {
my
$self
=
shift
;
my
$args
=
shift
;
$self
->{__TT_ARGS__} =
$args
;
}
sub
tt_args {
my
$self
=
shift
;
return
$self
->{__TT_ARGS__};
}
sub
generate {
my
$self
=
shift
;
my
$params
=
shift
;
my
$class_file
=
$params
->{class_file};
my
$ast
=
$params
->{ast};
my
$trace_level
=
defined
$params
->{trace_level} ?
$params
->{trace_level} :
$self
->{trace_level};
$self
->{type_caster} =
$params
->{type_caster} || Java::Javap::TypeCast->new();
$self
->_cast_names(
$ast
);
$ast
->{method_list} =
$self
->_get_unique_methods(
$ast
);
$ast
->{constant_list} = [
grep
{
$_
->{body_element} eq
'constant'
} @{
$ast
->{contents}} ];
my
$template
=
$self
->_get_template(
$ast
);
my
@prologue
=
$self
->_get_prologue(
$ast
);
my
$tt
= Template->new(
$self
->tt_args );
my
$tt_vars
= {
ast
=>
$ast
,
gen_time
=>
scalar
localtime
(),
version
=>
$Java::Javap::VERSION
,
class_file
=>
$class_file
,
type_caster
=>
$self
->{type_caster},
javap_command
=>
$params
->{javap_command},
prologue
=> \
@prologue
,
};
my
$retval
;
$tt
->process( \
$template
,
$tt_vars
, \
$retval
)
or
die
"Error processing template: "
.
$tt
->error();
return
$retval
;
}
sub
_cast_names {
my
$self
=
shift
;
my
$ast
=
shift
;
my
$type_caster
=
$self
->{type_caster};
my
$class_parent
=
defined
$ast
->{parent} ?
$type_caster
->cast(
$ast
->{parent}) :
''
;
$ast
->{cast_parent} = (
$class_parent
eq
'Mu'
) ?
''
:
$class_parent
;
my
@class_implements
=
map
{
$type_caster
->cast(
$_
) } @{
$ast
->{implements} };
$ast
->{cast_implements} = \
@class_implements
;
foreach
my
$element
(@{
$ast
->{contents}}) {
if
(
$element
->{body_element} =~ /^(method|constructor)$/) {
foreach
my
$arg
(@{
$element
->{args}}) {
$arg
->{perl_type_name} =
$type_caster
->cast(
$arg
->{name});
}
}
$element
->{type}->{perl_type_name} =
$type_caster
->cast(
$element
->{type}->{name})
if
ref
$element
->{type};
}
}
sub
_get_unique_methods {
my
$self
=
shift
;
my
$ast
=
shift
;
my
%meth
;
foreach
my
$element
(@{
$ast
->{contents}}) {
next
unless
$element
->{body_element} =~ /^(method|constructor)$/;
my
$signature
=
$element
->{name};
next
if
$signature
=~ /\$/;
foreach
my
$arg
(@{
$element
->{args}}) {
$signature
.= ((
$arg
->{array_text} =~ /Array of/) ?
'@'
:
'$'
) .
"$arg->{perl_type_name}, "
;
}
$signature
.=
" --> "
. (
$element
->{type}->{array_text} =~ /Array of/)
?
'Array'
:
$element
->{type}->{perl_type_name};
$meth
{
$signature
} =
$element
;
}
my
@methods
=
sort
{
(
$b
->{body_element} eq
'constructor'
) <=> (
$a
->{body_element} eq
'constructor'
) or
$a
->{name} cmp
$b
->{name} or
@{
$a
->{args}} <=> @{
$b
->{args}}
}
values
%meth
;
return
\
@methods
;
}
sub
_get_type_casts {
my
$self
=
shift
;
my
$type_file
=
shift
;
}
sub
_get_prologue {
my
$self
=
shift
;
my
$ast
=
shift
;
my
$type_caster
=
$self
->{type_caster};
my
$trace_level
=
defined
$self
->{trace_level} ?
$self
->{trace_level} : 0;
my
%perl_types
;
$perl_types
{
$ast
->{cast_parent} }{parent} = 1
if
$ast
->{cast_parent};
$perl_types
{
$_
}{implements} = 1
for
@{
$ast
->{cast_implements} };
foreach
my
$element
(@{
$ast
->{contents}}) {
if
(
$element
->{body_element} =~ /^(method|constructor)/) {
foreach
my
$arg
(@{
$element
->{args}}) {
$perl_types
{
$arg
->{perl_type_name}}{arg}++;
}
}
$perl_types
{
$element
->{type}->{perl_type_name} }{
return
}++
if
ref
$element
->{type};
}
warn
"$ast->{perl_qualified_name} references types: @{[ keys %perl_types ]}\n"
if
$trace_level
>= 3;
for
my
$perl_type
(
keys
%perl_types
) {
delete
$perl_types
{
$perl_type
}
if
$perl_builtin_types
->{
$perl_type
}
or
$perl_type
eq
$ast
->{perl_qualified_name}
or
$perl_type
=~ /\$/
or
$perl_type
eq
'void'
;
}
warn
"$ast->{perl_qualified_name} needs to load: @{[ keys %perl_types ]}\n"
if
$trace_level
>= 3;
my
(
@decl_class
,
@load_class
);
while
(
my
(
$type
,
$usage
) =
each
%perl_types
) {
if
(
$usage
->{parent} ||
$usage
->{implements}) {
push
@load_class
,
$type
;
next
;
}
push
@decl_class
,
$type
;
}
return
(
map
({
"class $_ { ... };"
}
sort
@decl_class
),
map
({
"use $_;"
}
sort
@load_class
)
);
}
sub
_get_template {
my
$self
=
shift
;
my
$ast
=
shift
;
my
$prologue
=
$self
->_get_template_prologue;
my
$kind
=
$ast
->{ class_or_interface };
$kind
=
'interface'
;
my
$method
=
"_get_template_for_$kind"
;
return
$prologue
.
$self
->
$method
(
$ast
);
}
sub
_get_template_prologue {
return
<<
'EO_Template'
;
[% BLOCK file_header %]
[% END %]
[% BLOCK method_arg %]
[% arg.perl_type_name %] [% arg.array_text.search(
'Array of'
) ?
'@'
:
'$'
%]v[% arg_counter %],
[% END %]
[% BLOCK method_all_args %]
[% arg_counter = 0 %]
[% FOREACH arg IN elem.args %][% arg_counter = arg_counter + 1 %]
[% INCLUDE method_arg %]
[% END %]
[% END %]
[% BLOCK method_returns %]
[%- IF ret.name !=
'void'
%] --> [% ret.array_text.search(
'Array of'
) ?
'Array '
: ret.perl_type_name %]
[% END %]
[% END %]
[% BLOCK method_whole %]
[% ast.methods.${ elem.name } > 1 ?
'multi '
:
''
-%]
method [% elem.name -%]
(
[% INCLUDE method_all_args elem = elem %]
[% INCLUDE method_returns ret = elem.type %]
) { ... }
[%- IF elem.throws.size %]
[% END %]
[% BLOCK constant_whole %]
method [% elem.name %] (--> [% elem.type.perl_type_name %]) is export {
[%- IF elem.type.perl_type_name ==
'Str'
%] [% elem.value | perl6_string_literal %]
[%- ELSIF elem.type.perl_type_name ==
'Int'
or elem.type.perl_type_name ==
'Num'
%] [% elem.value %]
[%- ELSE %] ... [% END -%] }
[% END %]
EO_Template
}
sub
_get_template_for_interface {
return
<<
'EO_Template'
;
[% PROCESS file_header %]
[% FOREACH prologue_item IN prologue %]
[%+ prologue_item +%]
[% END %]
role [% ast.perl_qualified_name %]
[%- IF ast.cast_parent !=
''
%] does [% ast.cast_parent %] [% END -%]
[%- IF ast.cast_implements.size > 0 %] does [% ast.cast_implements.
join
(
" does "
) %] [% END -%]
{
[% FOREACH element IN ast.constant_list %]
[% NEXT IF element.name.search(
'\$'
) %]
[% INCLUDE constant_whole elem = element %]
[% END; ast.constant_list.size ?
"\n"
:
""
%]
[% FOREACH element IN ast.method_list %]
[% INCLUDE method_whole elem = element %]
[% END %]
};
EO_Template
}
sub
_get_template_for_class {
return
<<
'EO_Class_Template'
;
[% PROCESS file_header %]
[% FOREACH prologue_item IN prologue %]
[%+ prologue_item +%]
[% END %]
class [% ast.perl_qualified_name %]
[%- ast.cast_parent ==
''
?
''
:
' is '
%][% ast.cast_parent -%]
[%- IF ast.cast_implements.size > 0 %] does [% ast.cast_implements.
join
(
" does "
) %] [% END -%]
{
[% FOREACH element IN ast.constant_list %]
[% NEXT IF element.name.search(
'\$'
) %]
[% INCLUDE constant_whole elem = element %]
[% END; ast.constant_list.size ?
"\n"
:
""
%]
[% FOREACH element IN ast.method_list %]
[% INCLUDE method_whole elem = element %]
[% END %]
};
EO_Class_Template
}
1;