open VTABLE, ">vtable.h" or die $!;
print VTABLE "struct vtable {\n";
while (<DATA>) { 
    next if /^#/; 
    unless (/^\s*(.*?)\s+(\S+)\(([^\)]+)\)/) { # urgh.
        print "Malformed line in spec $.: $_";
        next;
    } else {
        my ($rv, $name, $args) = ($1,$2,$3);
        s/\[.*\]/, .../;
        $args =~ s/\[.*\]/, .../;
        $vfunc{$name} = {
            rv => $rv,
            special => s/##//,
            args => $args,
            proto => $_
            
        };
        print VTABLE;
    }
}
print VTABLE "};\n\ntypedef struct vtable VTABLE;\n";

my $inmethod =0;
my %classes;

while (<STDIN>) {
    if (/^\s+/) {print if $inmethod; next;}
    if (/^$/ and $inmethod) { print "} \n\n"; $inmethod =0; next; }
    if (/CLASS=(\S+) METHOD=(\S+)/) {
        my ($class, $method) = ($1,$2);
        my $funcname = "v_$class\_$method";
        if (!$vfunc{$method}) {
            die "Method $method does not exist at line $. of input\n";
        }
        if ($inmethod) { print "}\n\n"} 
        print $vfunc{$method}{rv}, " $funcname (", $vfunc{$method}{args},") {\n";
        $inmethod=1;
        unless ($classes{$class}) {
            $classes{$class} = { map {$_ => 0 } keys %vfunc };
        }
        if ($classes{$class}{$method}++ == 1) {
            die "Method $method in class $class already defined by line $. of input\n";
        }
    } elsif (/#if (.*)/) {
        # Handle conditional code.
    }
};

print "}\n\n" if $inmethod;
for my $class (keys %classes) {
    my @undone = grep { $classes{$class}{$_} == 0} keys %vfunc;
    warn "WARNING: $class class leaves the following methods undefined: @undone\n" if @undone;
    print VTABLE "VTABLE $class\_vtable = {\n\t";
    print VTABLE join (",\n\t", map { "v_$class\_$_" } keys %vfunc);
    print VTABLE "\n};";
}

__DATA__
# These are functions that every vtable must provide.
  IV            type(PMC pmc[, subtype]);
  STR		name(PMC pmc[, key]);
  PMC	        new(PMC pmc[, key]);
  void	        clone(PMC source, PMC dest[, flags[,key]);
  void		morph(PMC pmc, IV type[, key]);
  BOOL		move_to(void *, PMC);
  IV		real_size(PMC[, key]);
  void		destroy(PMC[, key]);
  INT		get_integer(PMC[, key]); ##
  NUM           get_number(PMC[, key]); ##
  STR		get_string(PMC[, key]); ##
  BOOL		get_bool(PMC[, key]); ##
  void *	get_value(PMC[, key]);
  BOOL		is_same(PMC, PMC[, key]);
  void		set_integer(PMC, INT[, key]); ##
  void		set_number(PMC, NUM[, key]); ##
  void		set_string(PMC, STR[, key]); ##
  void		set_value(PMC, void *[, key]);
  void		add(PMC, PMC, PMC[, key]); ##
  void		subtract(PMC, PMC, PMC[, key]); ##
  void		multiply(PMC, PMC, PMC[, key]); ##
  void		divide(PMC, PMC, PMC[, key]); ##
  void		modulus(PMC, PMC, PMC[, key]); ##
  void		concatenate(PMC, PMC, PMC[, key]); ##
  void		is_equal(PMC, PMC[,key]); ##
  void		logical_or(PMC, PMC, PMC[, key]); ##
  void		logical_and(PMC, PMC, PMC[, key]); ##
  void		logical_not(PMC, PMC[,key]); ##
  void		match(PMC, PMC, REGEX[, key]);
  void		repeat(PMC, PMC, PMC[, key]); ##
