cvsuser 03/10/07 08:40:38
Modified: . MANIFEST
Added: classes pmc2c2.pl
lib/Parrot Pmc2c.pm
Log:
new pmc compiler
* experimental of course
* no Makefile integration
* not too much docs yet
* dynamic pmc handling is missing
Revision Changes Path
1.449 +2 -0 parrot/MANIFEST
Index: MANIFEST
===================================================================
RCS file: /cvs/public/parrot/MANIFEST,v
retrieving revision 1.448
retrieving revision 1.449
diff -u -w -r1.448 -r1.449
--- MANIFEST 7 Oct 2003 05:07:14 -0000 1.448
+++ MANIFEST 7 Oct 2003 15:40:24 -0000 1.449
@@ -58,6 +58,7 @@
classes/perlstring.pmc []
classes/perlundef.pmc []
classes/pmc2c.pl []
+classes/pmc2c2.pl []
classes/pointer.pmc []
classes/ref.pmc []
classes/retcontinuation.pmc []
@@ -2067,6 +2068,7 @@
lib/Parrot/PackFile/FixupTable.pm [devel]
lib/Parrot/PakFile2.pm [devel]
lib/Parrot/PakFile2.xs [devel]
+lib/Parrot/Pmc2c.pm [devel]
lib/Parrot/String.pm [devel]
lib/Parrot/Test.pm [devel]
lib/Parrot/Types.pm [devel]
1.1 parrot/classes/pmc2c2.pl
Index: pmc2c2.pl
===================================================================
#! /usr/bin/perl -w
#
# pmc2c2.pl
#
# Generate a C source and a header
# file from the methods defined in a .pmc file.
#
=head1 NAME
pmc2c2.pl - V2 PMC compiler
=head1 SYNOPSIS
=over 4
=item perl classes/pmc2c2.pl --dump classes/foo.pmc ...
Create F<classes/foo.dump>
=item perl pmc2c2.pl --vtable
Create F<vtable.dump>
=item perl classes/pmc2c2.pl --tree classes/*.pmc
Print class tree of given pmcs.
=item perl classes/pmc2c2.pl -c classes/foo.pmc ...
TODO create classes/foo.{c,h} from classes/foo.dump
=back
=cut
use FindBin;
use lib 'lib';
use lib "$FindBin::Bin/..";
use lib "$FindBin::Bin/../lib";
use Parrot::Vtable;
use Parrot::Pmc2c;
use strict;
use Data::Dumper;
use Getopt::Long;
my %opt;
main();
sub dump_default {
my $default = parse_vtable("$FindBin::Bin/../vtable.tbl");
open(VTD, ">vtable.dump") or die "Can't write vtable.dump";
my %vt;
$vt{flags} = {};
$vt{pre} = '';
$vt{post} = '';
my %meth_hash;
my $i = 0;
foreach my $entry (@$default) {
$meth_hash{$entry->[1]} = $i++;
push ( @{$vt{methods}},
{
parameters => $entry->[2],
meth => $entry->[1],
type => $entry->[0],
section => $entry->[3]
});
}
$vt{'has_method'} = \%meth_hash;
my $Dumper = Data::Dumper->new([\%vt], [qw(class)]);
$Dumper->Indent(3);
print VTD $Dumper->Dump();
close VTD;
}
sub count_newlines {
return scalar(() = $_[0] =~ /\n/g);
}
sub extract_balanced {
my $balance = 0;
my $lines = 0;
for(shift) {
s/^(\s+)//;
$lines += count_newlines($1);
/^\{/ or die "bad block open: ".substr($_,0,10),"..."; # }
while(/(\{)|(\})/g) {
if($1) {
$balance++;
} else { # $2
--$balance or return (substr($_, 0, pos, ""), $_, $lines);
}
}
die "Badly balanced" if $balance;
}
}
sub parse_flags {
my $c = shift;
$$c =~ s/^(.*?^\s*)pmclass ([\w]*)//ms;
my ($pre, $classname) = ($1, $2);
my %has_value = ( does => 1, extends => 1 );
my %flags;
# look through the pmc declaration header for flags such as noinit
while ($$c =~ s/^(?:\s*)(\w+)//s) {
if ($has_value{$1}) {
my $what = $1;
if (s/^(?:\s+)(\w+)//s) {
$flags{$what}{$1} = 1;
}
else {
die "Parser error: no value for '$what'";
}
}
else {
$flags{$1} = 1;
}
}
# setup some defaults
if ($classname eq 'OrderedHash') {
#$flags{extends}{PerlHash} = 1;
}
if ($classname ne 'default') {
$flags{extends}{default} = 1 unless $flags{extends};
$flags{does}{scalar} = 1 unless $flags{does};
}
($pre, $classname, \%flags);
}
sub parse_pmc {
local $_ = shift;
my $signature_re = qr{
^
(?: #blank spaces and comments and spurious semicolons
[;\n\s]*
(?:/\*.*?\*/)? # C-like comments
)*
(\w+\**) #type
\s+
(\w+) #method name
\s*
\(([^\(]*)\) #parameters
}sx;
my ($pre, $classname, $flags) = parse_flags(\$_);
my $lineno = 1;
$lineno += count_newlines($pre);
my ($classblock, $post, $lines) = extract_balanced($_);
$classblock = substr($classblock, 1,-1); # trim out the { }
my (@methods, %meth_hash);
while ($classblock =~ s/($signature_re)//) {
$lineno += count_newlines($1);
my ($type, $methodname, $parameters) = ($2,$3,$4);
my ($methodblock, $rema, $lines) = extract_balanced($classblock);
$lineno += $lines;
$methodblock = "" if $opt{nobody};
# name => method idx mapping
$meth_hash{$methodname} = scalar @methods;
push @methods,
{ 'meth' => $methodname,
'body' => $methodblock,
'line' => $lineno,
'type' => $type,
'parameters' => $parameters
};
$classblock = $rema;
$lineno += count_newlines($methodblock);
}
return ( $classname, {
'pre' => $pre,
'flags' => $flags,
'methods' => [EMAIL PROTECTED],
'post' => $post,
'class' => $classname,
'has_method' => \%meth_hash
}
);
}
# make a linear list of class->{parents} array
sub gen_parent_list {
my ($this, $all) = @_;
my @todo = ($this);
my $class = $all->{$this};
while (@todo) {
my $n = shift @todo;
my $sub = $all->{$n};
next if $n eq 'default';
foreach my $parent (keys %{$sub->{flags}{extends}}) {
next if exists $class->{has_parent}{$parent};
if (!$all->{$parent}) {
my $pf = lc $parent;
$all->{$parent} = read_dump("classes/$pf.pmc");
}
$class->{has_parent}{$parent} = { %{$all->{$parent}{has_method} }};
push(@todo, $parent);
push(@{ $class->{parents} }, $parent);
}
}
}
sub dump_1_pmc {
my $file = shift;
$file =~ s/\.\w+/.pmc/;
print "Reading $file\n" if $opt{verbose};
open F, "<$file" or die "Can't read '$file'";
local $/;
my $contents = <F>;
close F;
return parse_pmc($contents);
}
sub gen_super_meths {
my ($self, $vt) = @_;
# look through all meths in class and locate the nearest parent
foreach my $entry (@{ $vt->{methods} } ) {
my $meth = $entry->{meth};
next if exists $self->{super}{$meth};
foreach my $pname (@{ $self->{parents} } ) {
if (exists $self->{has_parent}{$pname}{$meth} ) {
$self->{super}{$meth} = $pname;
last;
}
}
unless (exists $self->{super}{$meth}) {
$self->{super}{$meth} = 'default';
}
}
}
sub add_defaulted {
my ($class, $vt) = @_;
my $i = @{ $class->{methods} };
foreach my $e ( @{$vt->{methods}} ) {
my $meth = $e->{meth};
$class->{super}{$meth} = 'default';
}
}
sub dump_pmc {
my @files = @_;
my %all;
foreach my $file (@files) {
my ($class, $res) = dump_1_pmc($file);
$res->{file} = $file;
$all{$class} = $res;
}
my $vt = read_dump("vtable.pmc");
if (!$all{default}) {
$all{default} = read_dump("classes/default.pmc");
}
add_defaulted($all{default}, $vt);
foreach my $name (keys %all) {
my $dump;
my $file = $all{$name}->{file};
($dump = $file) =~ s/\.\w+/\.dump/;
gen_parent_list($name, \%all);
my $class = $all{$name};
gen_super_meths($class, $vt);
my $Dumper = Data::Dumper->new([$class], [qw(class)]);
$Dumper->Indent(1);
open PMD, ">$dump" or die "Can't write '$dump";
print PMD $Dumper->Dump;
close PMD;
}
}
sub read_dump {
my $file = shift;
my $dump;
($dump = $file) =~ s/\.\w+/.dump/;
unless ( -e $dump) {
if ($dump =~ m!^classes/!) {
$dump =~ s!^classes/!!;
}
elsif ($dump =~ m!^vtable!) {
$dump = "../vtable.dump";
}
}
print "Reading $dump\n" if $opt{verbose};
open D, "<$dump" or die "Can't read '$dump'";
local $/;
my $contents = <D>;
close D;
my $class;
# $class => { ... };
eval $contents;
die $@ if $@;
$class;
}
sub print_tree {
my ($depth, @files) = @_;
foreach my $file (@files) {
my $class = read_dump($file);
my $name = $class->{class};
print " " x $depth, $name, "\n";
foreach my $parent (keys %{$class->{flags}{extends}}) {
my $pmc = "classes/" . lc($parent) . ".pmc";
print_tree($depth + 1, $pmc);
}
}
}
sub gen_c {
my (@files) = @_;
foreach my $file (@files) {
my $class = read_dump($file);
# finally append vtable.dump
$class->{vtable} = read_dump("vtable.pmc");
my $generator = Parrot::Pmc2c->new($class, \%opt);
print Data::Dumper->Dump([$generator]) if $opt{debug} > 1;
my $hout = $generator->gen_h($file);
print $hout if $opt{debug};
my $h;
($h = $file) =~ s/\.\w+/.h/;
$h =~ s/(\w+)\.h/pmc_$1.h/;
print "Writing $h\n" if $opt{verbose};
open H, ">$h" or die "Can't write '$h";
print H $hout;
close H;
my $cout = $generator->gen_c($file);
print $cout if $opt{debug};
my $c;
($c = $file) =~ s/\.\w+/.c/;
print "Writing $c\n" if $opt{verbose};
open C, ">$c" or die "Can't write '$c";
print C $cout;
close C;
}
}
sub main {
my ($default, $dump, $gen_c, $result, $tree, $debug, $verbose, $nobody,
$nolines);
$result = GetOptions(
"vtable" => \$default,
"dump" => \$dump,
"c|gen-c" => \$gen_c,
"tree" => \$tree,
"nobody" => \$nobody,
"nolines" => \$nolines,
"debug+" => \$debug,
"verbose+" => \$verbose,
);
$opt{debug} = $debug || 0;
$opt{verbose} = $verbose || 0;
$opt{nobody} = $nobody || 0;
$opt{nolines} = $nolines || 0;
$default and do {
dump_default();
exit;
};
$dump and do {
dump_pmc(@ARGV);
exit;
};
$tree and do {
print_tree(0, @ARGV);
exit;
};
$gen_c and do {
gen_c(@ARGV);
exit;
};
}
# vim: expandtab shiftwidth=4:
1.1 parrot/lib/Parrot/Pmc2c.pm
Index: Pmc2c.pm
===================================================================
package Parrot::Pmc2c;
use vars qw(@EXPORT_OK @writes %writes );
use base qw( Exporter );
@EXPORT_OK = qw(gen_c gen_h);
BEGIN {
@writes = qw(STORE PUSH POP SHIFT UNSHIFT DELETE);
@[EMAIL PROTECTED] = (1) x @writes;
};
sub does_write($$) {
my ($meth, $section) = @_;
exists $writes{$section} || $meth eq 'morph';
}
sub get_vtable_section() {
my $self = shift;
# make a hash of all method names containing vtable section
my $vt = $self->{vtable};
foreach my $entry (@{ $vt->{methods} } ) {
$self->{all}{$entry->{meth}} = $entry->{section};
}
}
sub make_const() {
my ($self, $class) = @_;
my $const = bless {}, $class . '::Const';
$self->{const} = $const;
my @methods = @{ $self->{methods} };
# copy super
$const->{super} = { %{ $self->{super} } };
my $i;
foreach my $entry (@methods) {
my $meth = $entry->{meth};
if (does_write($meth, $self->{all}{$meth})) {
# create methods if they write
$const->{has_method}{$meth} = $i++;
push @{ $const->{methods} }, {
meth => "$meth",
type => $entry->{type},
parameters => $entry->{parameters}
};
}
else {
# if not - they are inherited from $self
$const->{super}{$meth} = $self->{class};
}
}
# copy parent(s), prepend self as parrent
$const->{parents} = [ $self->{class}, @{ $self->{parents} } ];
# copy flags, set is_const
$const->{flags} = {is_const => 1, %{ $self->{flags} } };
delete $const->{flags}{const_too};
# set const in does
$const->{flags}{does}{const} = 1;
# set classname
$const->{class} = "Const" . $self->{class};
# and alias vtable
$const->{vtable} = $self->{vtable};
# set parentname
$const->{parentname} = $self->{class};
}
sub init() {
my ($self, $class) = @_;
$self->get_vtable_section();
$self->make_const($class) if $self->{flags}{const_too};
}
sub class_name {
my ($self, $class) = @_;
my %special = ( 'Ref' => 1, 'default' => 1 );
my $classname = $self->{class};
my $nclass = $class;
# bless object into different classes inheriting from
# Parrot::Pmc2c
if ($special{$classname}) {
$nclass .= "::" . $classname;
}
else {
$nclass .= "::Standard";
}
$nclass;
}
sub new {
my $this = shift;
my $class = ref($this) || $this;
my $self = shift;
$self->{opt} = shift;
$class = class_name($self, $class);
bless $self, $class;
$self->init($class);
$self;
}
sub count_newlines {
return scalar(() = $_[0] =~ /\n/g);
}
sub dont_edit() {
my ($self, $pmcfile) = @_;
return <<"EOC";
/*
* !!!!!!! DO NOT EDIT THIS FILE !!!!!!!
*
* This file is generated automatically from '$pmcfile'
* by $0.
*
* Any changes made here will be lost!
*
*/
EOC
}
sub decl() {
my ($self, $classname, $method, $for_header) = @_;
my $ret = $method->{type};
my $meth= $method->{meth};
my $args= $method->{parameters};
$args = ", $args" if $args =~ /\S/;
my ($extern, $newl, $semi, $interp, $pmc);
if ($for_header) {
$extern = "extern ";
$newl = " ";
$semi = ";";
$interp = $pmc = "";
}
else {
$extern = "";
$newl = "\n";
$semi = "";
$interp = ' interpreter';
$pmc = ' pmc';
}
return <<"EOC";
$extern$ret${newl}Parrot_${classname}_$meth(Parrot_Interp$interp, PMC*$pmc$args)$semi
EOC
}
sub includes() {
my $self = shift;
my $cout = "";
foreach my $parents ($self->{class}, @{ $self->{parents} } ) {
my $name = lc $parents;
$cout .= <<"EOC";
#include "pmc_$name.h"
EOC
}
"$cout\n";
}
sub full_arguments {
my $args = shift;
if ($args =~ /\S/) {
return "INTERP, SELF, $args";
} else {
return "INTERP, SELF";
}
}
sub rewrite_method ($$$$$) {
my ($class, $method, $super, $super_table) = @_;
local $_ = $_[4];
# Rewrite method body
my $supertype = "enum_class_$super";
die "$class defines unknown vtable method '$method'\n"
if ! defined $super_table->{$method};
my $supermethod = "Parrot_" . $super_table->{$method} . "_$method";
# Rewrite DYNSUPER(args...)
s/DYNSUPER\(\s*(.*?)\)/"Parrot_base_vtables[$supertype].$method(".full_arguments($1).")"/eg;
# Rewrite OtherClass.SUPER(args...)
s/(\w+)\.SUPER\(\s*(.*?)\)/"Parrot_${1}_$method(".full_arguments($2).")"/eg;
# Rewrite SUPER(args...)
s/SUPER\(\s*(.*?)\)/"$supermethod(".full_arguments($1).")"/eg;
# Rewrite DYNSELF.other_method(args...)
s/DYNSELF\.(\w+)\(\s*(.*?)\)/"pmc->vtable->$1(".full_arguments($2).")"/eg;
# Rewrite DYNSELF(args...). See comments above.
s/DYNSELF\(\s*(.*?)\)/"pmc->vtable->$method(".full_arguments($1).")"/eg;
# Rewrite OtherClass.SELF.other_method(args...)
s/(\w+)\.SELF\.(\w+)\(\s*(.*?)\)/"Parrot_${1}_$2(".full_arguments($3).")"/eg;
# Rewrite SELF.other_method(args...)
s/SELF\.(\w+)\(\s*(.*?)\)/"Parrot_${class}_$1(".full_arguments($2).")"/eg;
# Rewrite SELF -> pmc, INTERP -> interpreter
s/SELF/pmc/g;
s/INTERP/interpreter/g;
# now use macros for all rewritten stuff
s/\b(?:\w+)->vtable->(\w+)\(/ VTABLE_$1(/g;
return $_;
}
sub body
{
my ($self, $method) = @_;
my $cout = "";
my $classname = $self->{class};
my $pmc = lc($classname) .'.pmc';
my $meth = $method->{meth};
unless ($self->{opt}{nolines}) {
$cout .= <<"EOC";
#line $method->{line} "$pmc"
EOC
}
$cout .= $self->decl($classname, $method, 0);
my $body = $method->{body};
$body =~ s/^\t/ /mg;
$body =~ s/^[ ]{4}//mg;
my $super = $self->{super}{$meth};
$cout .= rewrite_method($classname, $meth, $super, $self->{super},
$body);
$cout .= "\n\n";
}
sub methods() {
my ($self, $line) = @_;
my $cout = "";
foreach my $method (@{ $self->{vtable}{methods}} ) {
next if $method->{meth} eq 'class_init';
my $ret = $self->body($method, $line);
$line += count_newlines($ret);
$cout .= $ret;
}
$cout;
}
sub init_func() {
my $self = shift;
my $cout = "";
return "" if exists $self->{flags}{noinit};
# gen C line comment
my $classname = $self->{class};
my $vtbl_flag = $self->{flags}{const_too} ?
'VTABLE_HAS_CONST_TOO' : $self->{flags}{is_const} ?
'VTABLE_IS_CONST_FLAG' : 0;
if (exists $self->{flags}{need_ext}) {
$vtbl_flag .= '|VTABLE_PMC_NEEDS_EXT';
}
my @meths;
foreach my $method (@{ $self->{vtable}{methods}} ) {
my $meth = $method->{meth};
if ($self->implements($meth)) {
push @meths, "Parrot_${classname}_$meth";
}
elsif (exists $self->{super}{$meth}) {
my $class = $self->{super}{$meth};
push @meths, "Parrot_${class}_$meth";
}
else {
push @meths, "Parrot_default_$meth";
}
}
my $methlist = join(",\n ", @meths);
my $isa = join(" ", $classname, @{ $self->{parents} });
$isa =~ s/\s?default$//;
my $does = join(" ", keys(%{ $self->{flags}{does} }));
my $n = exists $self->{has_method}{class_init} ?
$self->{has_method}{class_init} : -1;
my $class_init_code = $n >= 0 ? $self->{methods}[$n]{body} : "";
$class_init_code =~ s/INTERP/interp/g;
$cout .= <<"EOC";
void
Parrot_${classname}_class_init(Parrot_Interp interp, int entry)
{
struct _vtable temp_base_vtable = {
NULL, /* package */
enum_class_$classname, /* base_type */
NULL, /* whoami */
NULL, /* method_table */
$vtbl_flag, /* flags */
NULL, /* does_str */
NULL, /* isa_str */
NULL, /* extra data */
$methlist
EOC
$cout .= <<"EOC";
};
/*
* parrotio calls some class_init functions during its class_init
* code, so some of the slots might already be allocated
*/
if (!Parrot_base_vtables[entry]) {
temp_base_vtable.whoami = string_make(interp,
"$classname", @{[length($classname)]}, 0,
PObj_constant_FLAG|PObj_external_FLAG , 0);
temp_base_vtable.isa_str = string_make(interp,
"$isa", @{[length($isa)]}, 0,
PObj_constant_FLAG|PObj_external_FLAG , 0);
temp_base_vtable.does_str = string_make(interp,
"$does", @{[length($does)]}, 0,
PObj_constant_FLAG|PObj_external_FLAG , 0);
Parrot_base_vtables[entry] =
Parrot_clone_vtable(interp, &temp_base_vtable);
}
$class_init_code
} /* Parrot_${classname}_class_init */
EOC
$cout;
}
sub gen_c() {
my ($self, $file) = @_;
my $cout = $self->dont_edit($file);
$cout .= $self->{pre};
$cout .= $self->includes;
my $l = count_newlines($cout);
$cout .= $self->methods($l);
$cout .= $self->init_func;
if ($self->{const}) {
$cout .= $self->{const}->methods($l);
$cout .= $self->{const}->init_func;
}
$cout .= $self->{post};
$cout;
}
sub gen_h() {
my ($self, $file) = @_;
my $hout = $self->dont_edit($file);
my $classname = $self->{class};
# generat decls for all methods in this file
foreach my $meth (@{ $self->{vtable}{methods} } ) {
if ($self->implements($meth->{meth})) {
$hout .= $self->decl($classname, $meth, 1);
}
}
# class init decl
$hout .= <<"EOC";
void Parrot_${classname}_class_init(Parrot_Interp, int);
EOC
if ($self->{const}) {
$self = $self->{const};
my $classname = $self->{class};
$hout .= "\n/* Const */\n";
foreach my $meth (@{ $self->{methods} } ) {
$hout .= $self->decl($classname, $meth, 1);
}
$hout .= <<"EOC";
void Parrot_${classname}_class_init(Parrot_Interp, int);
EOC
}
$hout;
}
# true if this class generates code for $meth
sub implements
{
my ($self, $meth) = @_;
return exists $self->{has_method}{$meth};
}
# standard behavior
package Parrot::Pmc2c::Standard;
use base 'Parrot::Pmc2c';
sub body
{
my ($self, $method) = @_;
my $meth = $method->{meth};
# exisiting methods get emitted
if ($self->implements($meth)) {
my $n = $self->{has_method}{$meth};
return $self->SUPER::body($self->{methods}[$n]);
}
"";
}
# through excepton if meth writes
package Parrot::Pmc2c::Standard::Const;
use base 'Parrot::Pmc2c::Standard';
sub body {
my ($self, $method) = @_;
my $meth = $method->{meth};
return "" unless ($self->implements($meth));
my $decl = $self->decl($self->{class}, $method, 0);
my $classname = $self->{class};
my $parentname = $self->{parentname};
my $ret = $method->{type} eq 'void' ? "" : "return ($method->{type})0;";
my $cout = <<"EOC";
$decl {
EOC
if ($meth eq 'morph') {
$cout .= <<EOC;
if (Parrot_is_const_pmc(interpreter, pmc))
internal_exception(WRITE_TO_CONSTCLASS,
"$meth() in $classname");
else
Parrot_${parentname}_$meth(interpreter, pmc, type);
EOC
}
else {
$cout .= <<EOC;
internal_exception(WRITE_TO_CONSTCLASS,
"$meth() in $classname");
$ret
EOC
}
$cout .= <<"EOC";
}
EOC
$cout;
}
# Ref directs all unknow methods to the referee
package Parrot::Pmc2c::Ref;
use base 'Parrot::Pmc2c';
sub implements
{
1;
}
sub body
{
my ($self, $method, $line) = @_;
my $meth = $method->{meth};
# exisiting methods get emitted
if ($self->SUPER::implements($meth)) {
my $n = $self->{has_method}{$meth};
return $self->SUPER::body($self->{methods}[$n]);
}
my $parameters = $method->{parameters};
my $n=0;
my @args = grep {$n++ & 1 ? $_ : 0} split / /, $parameters;
my $arg = '';
$arg = ", ". join(' ', @args) if @args;
$parameters = ", $parameters" if $parameters;
my $body = "VTABLE_$meth(interpreter, PMC_ptr2p(pmc)$arg)";
my $ret = $method->{type} eq 'void' ? "$body;" : "return $body;" ;
my $decl = $self->decl($self->{class}, $method, 0);
my $l = "";
unless ($self->{opt}{nolines}) {
$l = <<"EOC";
#line $line "ref.c"
EOC
}
return <<EOC;
$l
$decl {
$ret
}
EOC
}
# default throws an execption for unknown meths
package Parrot::Pmc2c::default;
use base 'Parrot::Pmc2c';
sub implements
{
1;
}
sub body
{
my ($self, $method, $line) = @_;
my $meth = $method->{meth};
# exisiting methods get emitted
if ($self->SUPER::implements($meth)) {
my $n = $self->{has_method}{$meth};
return $self->SUPER::body($self->{methods}[$n]);
}
my $decl = $self->decl($self->{class}, $method, 0);
my $l = "";
my $ret = $method->{type} eq 'void' ? "" : "return ($method->{type})0;";
unless ($self->{opt}{nolines}) {
$l = <<"EOC";
#line $line "default.c"
EOC
}
return <<EOC;
$l
${decl} {
internal_exception(ILL_INHERIT,
"$meth() not implemented in class '%s'",
caller(interpreter, pmc));
$ret
}
EOC
}
# vim: expandtab shiftwidth=4:
1;