#!/usr/bin/perl -w ################################################################################ # # Copyright (C) 1998-2000, Ashley Winters # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # use Carp; $| = 1; umask 0; use strict; my %Types; my %Cast; my %Info; my %Input; my %Methods; my %Prototypes; my %IncCache; my %Inclusive; # All virtual methods in class and superclasses my %Exclusive; # Virtual methods first defined in this class my $Ext = '.pig'; my $Module = ''; my $Class = ''; my $Path = ''; my $File = ''; my $Line = 0; my(%Operator) = ( "=" => "newcopy", "()" => "run", "==" => "beq", "!=" => "bne", "*" => "bmul", "/" => "bdiv", "+" => "badd", "-" => "bsub", "neg" => "uneg", "*=" => "amul", "/=" => "adiv", "+=" => "aadd", "-=" => "asub", "<<" => "serialize", ">>" => "deserialize" ); my $Source = 'src'; my $Sourcedir; my $Libdir = 'lib'; my $VirtualHeader; my($Sourcefile, $Headerfile); my(@ClassList, %ConstantList); my @Vtbl; my @VtblIn; my %LinkList; my(@Modules, @I, @Include, @l, @L, @S, @r, @c); my($verbose, $silent, $pedantic); my $Indent; my $Method; my @sourcefiles; # list of files to save; #&main(); # Start of the program, bottom of the application sub whisper { print @_ unless $silent } sub say { whisper @_ unless $verbose } sub verbose { whisper $_[0] if $verbose } sub veryverbose { whisper $_[0] if $verbose && $verbose > 1 } sub source { print SOURCE @_ } sub header { print HEADER @_ } sub vheader { print VHEADER @_ } sub iheader { print IHEADER @_ } package PerlQt::Method; sub constructor { my $self = shift; return ($self->{'Name'} eq 'new'); } sub destructor { my $self = shift; return ($self->{'Name'} eq 'DESTROY'); } sub virtual { my $self = shift; return (defined($self->{'Virtual'}) && $self->{'Virtual'} ne 'static' && $self->{'Virtual'} ne 'variable'); } sub variable { my $self = shift; return (defined($self->{'Virtual'}) && $self->{'Virtual'} eq 'variable'); } sub abstract { my $self = shift; return (defined($self->{'Virtual'}) && $self->{'Virtual'} eq 'abstract'); } sub static { my $self = shift; return ($self->constructor || defined $self->{'Virtual'} && $self->{'Virtual'} eq 'static'); } sub private { my $self = shift; return ($self->{'Protection'} eq 'private'); } sub protected { my $self = shift; return ($self->{'Protection'} eq 'protected'); } sub public { my $self = shift; return ($self->{'Protection'} eq 'public'); } sub const { my $self = shift; return $self->{'Const'} || ""; } sub purpose { my $self = shift; return $self->{'Purpose'}; } sub perlonly { my $self = shift; return ($self->purpose eq '&'); } sub cpponly { my $self = shift; return ($self->purpose eq '^'); } sub everylang { my $self = shift; return ($self->purpose eq '*'); } package PerlQt::ClassInfo; sub alias { my $self = shift; return $self->{'Alias'}[0] if exists $self->{'Alias'}; return (); } sub define { my $self = shift; return @{$self->{'Define'}} if exists $self->{'Define'}; return (); } sub undefine { my $self = shift; return @{$self->{'Undef'}} if exists $self->{'Undef'}; return (); } sub include { my $self = shift; return @{$self->{'Include'}} if exists $self->{'Include'}; return (); } sub inherit { my $self = shift; return @{$self->{'Inherit'}} if exists $self->{'Inherit'}; return (); } sub virtual { my $self = shift; return @{$self->{'Virtual'}} if exists $self->{'Virtual'}; return (); } sub export { my $self = shift; return @{$self->{'Export'}} if exists $self->{'Export'}; return (); } sub class { my $self = shift; return (!exists $self->{'Class'} || $self->{'Class'}); } sub copy { my $self = shift; return (exists $self->{'Copy'} && $self->{'Copy'}); } package main; ################################################################################ # # &cpp_type($psuedotype) # # Takes a &slurped psuedo-type and converts it into its C++-equivalent # value. # # Returns a string which indicates the type of C++ argument $psuedotype # represents. # sub cpp_type { my $arg = shift; $arg =~ s/\{.*\}//; $arg =~ s/=.*//; $arg =~ s/^\s*//; $arg =~ s/\s*$//; return $arg; } ################################################################################ # # &polyname($proto) # # Enhance the method-name of a prototype to indicate argument number and # types. # # Returns a string which can be used to compare two prototypes and check for # an exact C++ inheritance-override match. # sub polyname { my $proto = shift; my $name; # cpp_type($proto->{'Returns'}); $name = $proto->{'Method'}; $name .= "("; my $x = 0; for my $argname (@{$proto->{'Arguments'}}) { next unless $x++ || $proto->static; my $arg = cpp_type($argname); $name .= "," if ($x - (1 - $proto->static)) > 1; $name .= $arg; } $name .= ")"; $name =~ s/\bconst\b//g; $name =~ s/\s+//g; $name .= 'static' if $proto->static; return $name; } ################################################################################ # # &getline(\*fileglob) # # Read a single prototype from $fileglobref for &slurp. The new prototype is # saved in $_. Erroneous prototypes are passed through without warning. # # The line is saved in $_. The line-number (for debugging) is saved in $line. # Returns true while not EOF. # # This is where any C++ => pig translation would need to take place. # &slurp is a sacred function, and should not be touched by mere mortals. # &getline is far more useful to the huddled masses yearning to add features. # sub getline { my $handle = shift; $Line = 0 if $Line eq "EOF"; $_ = readline(*$handle); $Line++; s~//.*~~ if $_; # remove comments unless(defined $_ && !eof(*$handle)) { $Line = "EOF"; return 0 unless defined $_; } return 1; } ################################################################################ # # &slurp(\*fileglob) # # Read from $fileglobref, parse it, save it in usable data-structures. # # sub slurp { my $source = shift; my(@include, @define, @undef, @alias); my($class, $protection); my $info; PARSE: while(getline($source)) { warn "Missing newline in $File ($Line): $_\n" unless chomp || !$pedantic; ########################### This section needs some cleanup if(/^\#(include|define|undef)\s*(.*)$/) { my $type = $1; my $arg = $2; if($type eq 'include') { #include if($arg =~ /^\<(.*)\>$/) { push @include, $1; } else { warn "Bad \#include directive [$_] in $File ($Line)\n"; } } elsif($type eq 'define') { #define THIS or #define THIS that if($arg =~ /^(\w+)(\s+.+|)$/) { push @define, ($2 ? "$1=$2" : $1); } else { warn "Bad \#define directive [$_] in $File ($Line)\n"; } } elsif($type eq 'undef') { #undef THIS if($arg =~ /^(\w+)\s*$/) { push @undef, $arg; } else { warn "Bad \#undef directive [$_] in $File ($Line)\n"; } } next PARSE; } # s/\#.*//; s/^\#/^/; s/^\/\//\&/; s/;.*/;/; if(/^\s*enum\s+\w*\s*\{/) { # begin enumeration my $enum = $_; until($enum =~ /\}\;/) { last PARSE unless getline($source); warn "Missing newline in $File ($Line): $_\n" unless chomp || !$pedantic; $enum .= $_; } $enum =~ /^\s*enum\s+\w*\s*\{(.*?)\}\;/; my(@enum) = map { /(\w+)/; $1 } split /,\s*/, $1; if($class) { for my $e (@enum) { $Info{$class}{'Constant'}{$e}{'Type'} = 'enum'; } # print "Complete enum: $class\::@enum\n"; } else { for my $e (@enum) { $Info{$Class}{'Global'}{$e}{'Type'} = 'enum'; } # print "Global enum: $Class\::@enum\n"; } next PARSE; } if(/^\s*(?:extern\s+)?const\s+(\w+\s*?(?:\*|\&)*)\s*(\w+)\s*\;$/) { if($class) { $Info{$class}{'Constant'}{$2}{'Type'} = $1; # print "Const $1 $class\::$2\n"; } else { $Info{$Class}{'Global'}{$2}{'Type'} = $1; # print "Global $1 $Class\::$2\n"; } next PARSE; } if(/^ (suicidal\s+|) # don't delete on destroy? (virtual\s+|) # virtual class (class|struct|namespace)\s+ # must have the word 'class' or 'struct' (\w+)\s* # (virtual)? class QClass (:.*|) # (virtual)? class QClass : QSuper \{\s* # (virtual)? class QClass : QSuper { $/x) { my($suicidal, $v, $t, $c, $s) = ($1, $2, $3, $4, $5); $class = $c; $Info{$class} = $Input{$class}{"\$"}{$class} = {}; $info = bless $Info{$class}, 'PerlQt::ClassInfo'; push @{$info->{'Virtual'}}, $class if $v; $info->{'Copy'} = 1 if $t eq 'struct'; $info->{'Class'} = 0 if $t eq 'namespace'; $info->{'Suicidal'} = 1 if $suicidal; $protection = 'public'; if($s) { my(@list) = split(',', $s); for my $super (@list) { if($super =~ /^:?\s*(virtual\s+|)(\w+)\s*$/) { push @{$info->{'Inherit'}}, $2; push @{$info->{'Virtual'}}, $2 if $1; } } } next; } if(/^\}(.*);\s*$/) { for my $alias (split(',', $1)) { $alias =~ s/^\s*(.*?)\s*$/$1/; push @alias, $alias; } # Since this is the end of the class, we populate $Info{$class} $Info{$class}{'Alias'} = \@alias if @alias; $Info{$class}{'Include'} = \@include if @include; $Info{$class}{'Define'} = \@define if @define; $Info{$class}{'Undef'} = \@undef if @undef; $class = undef; next; } if($class) { if(/^\s*(public|protected|private):\s*$/) { $protection = $1; next; } s/^(\*|\&|\^|)\s* ((?:(?:virtual|abstract|static|variable)\s+)?) (const\s+|) ((?:\w+\s*[\*\&]+\s*|[\w:\<\>\*]+\s*\*?[^\w\(\:\{]|[\w:]+\s+)?) (\{.*?\}\s*|) (~?\w+\s*\(.*|operator.*) /$1$protection $2$3$4$5$class\::$6/x; # print "$_\n"; } ########################### End of nasty section if(/^ # Method parsing regex (\+|-|) # $1 => method-diffs. Defaults to "+" if unspecified (\*|\^|\&|) # $2 => purpose. Defaults to "*" (.*?) # $3 => protection? attribute? return-type? \s* # C++ type voodoo =~ m!type\s*(\**\s*)*&?\s*! (\w+):: # $4 => class-name (operator\s*\S+ # allow "operator ()" without choking the regex |~?\w+) # $5 => method-name, allow "~destructor" \s* # allow "type class::method ()" \((.*?)\) # $6 => end this argument-list with a space or a ; (?:\s+([:\w].*))? # $7 => post-arg modifiers and colon-substitutes ; # EVERY method must be terminated with a semi-colon! (?:\s*\#.*)? # toss any comments at the end of the line $/x) { my($sign, $purpose, $ret, $class, $method, $args, $mod) = ($1, $2, $3, $4, $5, $6, $7); my($perlname, $prot, $virt, $const, $sigslot, $code); my @args; # unified diff format, + means add, - means remove $sign = "+" unless $sign; # default to + (add) # "*" means visible in Perl and C++ # "&" means visible in Perl # "^" means visible in C++ $purpose = "*" unless $purpose; # Class::~Class() => Class::DESTROY # Class::Class() => Class::new # Class::operator ?? () => Class::$operator{??} # Class::method(...) => Class::method $perlname = ($method =~ /^~/) ? "DESTROY" : ($method eq $class) ? "new" : ($method =~ /operator\s*(.*)$/) ? $Operator{$1} : $method; # ^public|protected|private virtual|abstract|static|variable ...+ if($ret =~ s/^(public|protected|private)\s*//) { $prot = $1 } if($ret =~ s/^(virtual|abstract|static|variable|)\s*//) { $virt = $1 if $1; } if($ret =~ s/^\.{3,}$//) { $ret = length($ret) - 3 } $prot = "public" unless defined $prot; # default to public # Class::Class() => Class *Class::Class() # Class::~Class() => void Class::~Class() unless($ret) { if($perlname eq "new") { $ret = "$class *" } elsif($perlname eq "DESTROY") { $ret = "void" } else { warn "No return-type [$_] in $File ($Line)\n" } } # Class::method() : $this->method(); # Class::method() signal; # Class::method() const; if($mod) { my $origmod = $mod; if($mod =~ s/\s*:\s*(.*)//) { $code = $1 } if($mod =~ s/\s*\b(slot|signal)\b\s*//) { $sigslot = $1 } if($mod =~ s/\s*\bconst\b\s*//) { $const = 1 } if($mod) { warn "Invalid method-modifier [$origmod] ($mod) " . "in $File ($Line)\n"; } } # Class::method(...) => Class::method(AV *); # Class::method(.....) => Class::method(SV *, SV *); # Class::method(type1 = Thing(6,7,8,9), that2 = this); # while($args =~ s/^,?\s*([^,\{]+)(\{.*\})?\s*//) { for my $arg (split(/,\s+/, $args)) { push @args, $arg; # if($arg eq "...") { # push @args, "..."; #"AV *"; # } elsif($arg =~ /^\.{4,}$/) { # push @args, ("SV *") x (length($arg) - 3); # } else { # } } # Class::method(int) => Class::method(Class *, int) # Class::method() const => Class::method(const Class *) if($perlname ne "new" && (!defined $virt || $virt ne "static")) { my $arg; $arg .= "const " if defined $const; $arg .= "$class *"; unshift @args, $arg; } my $info = bless { Name => $perlname, # Perl method-name File => \$File, # Filename (for warnings) Line => $Line, # Line-number (for warnings) Prototype => $_, # Actual prototype (for warnings) Diff => $sign, # Add or remove? Purpose => $purpose, # C++ or Perl or Both? Protection => $prot, # private/protected/public Virtual => $virt, # abstract/virtual/static Returns => $ret, # Return-type Class => $class, # Classname for *this and inheritance Method => $method, # Method-name for this->Method() Arguments => \@args, # Argument-list ref SigSlot => $sigslot, # signal/slot Const => $const, # const? Code => $code # Everything between the : and the ; }, 'PerlQt::Method'; push @{$Input{$Class}{'Proto'}}, $info; push @{$Methods{$Class}{$perlname}}, $info; $Prototypes{$Class}{&polyname($info)} = $info; } # end if(proto) elsif(/\S/) { warn "Invalid line $Line in $File: $_\n" unless /^\s*;$/; } } } use Cwd; sub arguments { my(@args) = @_; push @I, cwd . "/include"; push @L, cwd . "/lib"; for(my $i = 0; $i < @args; $i++) { if($args[$i] =~ /^-(.)(.*)/) { my($opt, $arg) = ($1, $2); if($opt eq 'v') { $verbose = 1; $verbose += length($arg) if $arg =~ /^v+$/; } elsif($opt eq 's') { $silent = 1; } elsif($opt eq 'p') { $pedantic = 1; } elsif($opt eq 'o') { $Source = $arg ? $arg : $args[++$i]; } elsif($opt eq 'I') { push @I, $arg ? $arg : $args[++$i]; } elsif($opt eq 'L') { push @L, $arg ? $arg : $args[++$i]; } elsif($opt eq 'S') { push @S, $arg ? $arg : $args[++$i]; } elsif($opt eq 'i' && $arg eq 'nclude') { push @Include, $args[++$i]; } elsif($opt eq 'l') { push @l, $arg ? $arg : $args[++$i]; } elsif($opt eq 'r') { my $a = $arg ? $arg : $args[++$i]; $a = "pig_$a"; push @l, $a; } elsif($opt eq 'c') { push @c, $arg ? $arg : $args[++$i]; } else { push @Modules, $args[$i]; } } else { push @Modules, $args[$i]; } } } sub find ($) { my $module = shift; my(@path) = ('.', @S); for my $path (@path) { return "$path/$module" if -d "$path/$module"; } local($") = "', '"; warn "Cannot find $module in ('@path') for $Class\n"; return undef; } sub ismod ($) { my $module = shift; my(@path) = ('.', @S); for my $path (@path) { return 1 if -e "$path/$module$Ext"; return 1 if -e "$path/$Module/$module$Ext"; } return 0; } sub findmod ($) { my $module = shift; my(@path) = ('.', @S); # print "$Module => $Class\n"; for my $path (@path) { # print "$path/$Module/$module\n"; return "$path/$module" if -e "$path/$module"; return "$path/$Module/$module" if -e "$path/$Module/$module"; } local($") = "', '"; warn "Cannot find $module in ('@path') for $Class\n"; return undef; } sub mklibdir { mkdir $Libdir, 0755 unless -d $Libdir; } sub mksrcdir ($) { my $module = shift; my $srcdir = "$Source/$module"; mkdir $Source, 0755 unless -d $Source; mkdir $srcdir, 0755 unless -d $srcdir; return $srcdir; } #sub manifest ($) { print MANIFEST $_[0] . "\n" } #sub makefile ($) { print MAKEFILE "\t$_[0]\$(OBJ_EXT)\n" } sub makefile ($) { push @sourcefiles, "$Sourcedir/$_[0]\$(OBJ_EXT)" } sub export ($) { print MODULEEXPORT $_[0] } #sub startmanifest { # unless(open MANIFEST, ">$Sourcedir/MANIFEST") { # warn "Cannot open $Sourcedir/MANIFEST for writing: $!"; # return; # } # # manifest "MANIFEST"; #} # #sub endmanifest { # close MANIFEST; #} # #sub startmakefile { # unless(open MAKEFILE, ">$Sourcedir/Makefile.PL") { # warn "Cannot open $Sourcedir/Makefile.PL for writing: $!"; # return; # } # # manifest "Makefile.PL"; # # print MAKEFILE "use ExtUtils::MakeMaker;\n"; # print MAKEFILE "require 'perlqt.conf';\n\n"; # # print MAKEFILE "WriteMakefile(\n"; # print MAKEFILE " 'NAME' => '$Module',\n"; # print MAKEFILE " 'VERSION_FROM' => 'perlqt.conf',\n"; # print MAKEFILE " 'CONFIGURE' => sub { return \\%PigConfig },\n"; # print MAKEFILE " 'OBJECT' => q{\n"; # # # for my $dir (@c) { # my $path = ($dir =~ m!^/!) ? $dir : "../../$dir"; # next unless opendir(PIGDIR, $dir); # my $file; # while(defined($file = readdir(PIGDIR))) { # if($file =~ /^(.*)\.c$/) { # symlink("$path/$file", "$Sourcedir/$file"); # manifest $file; # makefile $1; # } # } # closedir(PIGDIR); # } # # return 1; #} # #sub endmakefile { # print MAKEFILE " }\n);\n"; # close MAKEFILE; #} sub startmodulecode { unless(open MODULEEXPORT, ">$Sourcedir/pig_entry_$Module.c") { warn "Cannot open $Sourcedir/pig_entry_$Module.c for writing: $!"; return; } # manifest "$Module.entry.c"; makefile "$Sourcedir/pig_entry_$Module"; export qq'#include "pig.h"\n'; } sub endmodulecode { export "\n"; export "struct pig_classinfo PIG_module\[] = {\n"; for my $class (@ClassList) { my $suicidenote = $Info{$class}{'Suicidal'} ? "PIG_CLASS_SUICIDAL" : "0"; my $const = "0"; my $alias = $Info{$class}->alias || $class; $const = "PIG_${class}_const" if keys %{$Info{$class}{'Constant'}}; if($Info{$class}->class) { export qq' {\t"$class",\n\t"$alias",\n\tPIG_${class}_methods,\n\t$const,\n\tPIG_${class}_isa,\n\t'; export qq'PIG_${class}_tocast,\n\tPIG_${class}_fromcast,\n\t$suicidenote\n },\n'; } else { export qq' {\t"$class",\n\t"$alias",\n\tPIG_${class}_methods,\n\t$const,\n\t0,\n\t'; export qq'0,\n\t0,\n\t0\n },\n'; } } export " { 0, 0, 0, 0, 0, 0 }\n"; export "};\n\n"; export "struct pig_constant PIG_constant_$Module\[] = {\n"; for my $cinfo (keys %ConstantList) { export " { (void *)$cinfo, $ConstantList{$cinfo} },\n"; } export " { 0, 0 }\n"; export "};\n\n"; # export "struct pig_exportinfolist PIG_export\[] = {\n"; # for my $extern (sort { $ConstantList{$a}{'Name'} cmp $ConstantList{$b}{'Name'} } keys %ConstantList) { # my $cast = $ConstantList{$extern}{'Cast'}; # export qq' { "%$ConstantList{$extern}{"Name"}", $extern, '; # if($cast) { # $cast =~ s/^\s*const\s+//; # if($cast =~ /^\s*(\w+)/) { # export qq'"$1"'; # } else { # warn "$ConstantList{$extern}{'Cast'} is an unacceptable type"; # } # } else { # export "0"; # } # export " },\n"; # } # export " { 0, 0 }\n"; # export "};\n\n"; export "PIG_EXPORT_TABLE(PIG_$Module)\n"; # export "struct pig_symboltable PIG_export_$Module\[] = {\n"; for my $export (@Vtbl) { export " PIG_EXPORT_SUBTABLE(PIG_${export}_vtbl)\n"; # export " { 0, (void *)PIG_${export}_export_vtbl },\n"; } export "PIG_EXPORT_ENDTABLE\n\n"; # export " { 0, 0 }\n};\n\n"; close MODULEEXPORT; } sub info () { return $Info{$Class} } sub readtypemap { my $typemap = shift; unless(open TYPEMAP, $typemap) { die "Could not open typemap $typemap: $!\n"; } while() { s/\#.*//; if(/(.*\S)\s*=>\s*(.*\S)\s*/) { my $type = $1; $Types{$type} = $2; $Types{$type} =~ s/\s//g; # print "$typemap $_ [$.] => type $type => $Types{$type}\n"; } elsif(/^(\w+)\s*=\s*(.*)/) { $Cast{$1} = $2; # print "cast $1 => $2\n"; } } close TYPEMAP; } sub list ($) { my $path = shift; unless(opendir MODULEDIR, $path) { warn "Could not open $path for $Module: $!\n"; return (); } my(@c, @h, @pig); for my $file (sort readdir MODULEDIR) { push @c, $1 if $file =~ /(.+)\.c$/; push @h, $1 if $file =~ /(.+)\.h$/; push @pig, $1 if $file =~ /(.+)$Ext$/; # if($file =~ /^pig\..*$/) { # readtypemap("$path/$file"); # } } closedir MODULEDIR; # for my $c (@c) { manifest $c . '.c'; } # makefile $c } # for my $h (@h) { manifest $h . '.h' } return @pig; } sub loadmodule { my $class = shift; return if exists $Info{$class}; $File = findmod("$class$Ext"); # warn "Trying to find $class$Ext\n"; unless(open PIG, $File) { require Carp; Carp::confess("Cannot open $File for $class for reading: $!"); return; } my $ssalc = $Class; $Class = $class; slurp(\*PIG); if(!exists($Methods{$class}{'DESTROY'}) && $Info{$class}->class) { my $destroy = bless { Name => "DESTROY", File => \$File, Line => 0, Prototype => "$class\::~$class();", Diff => '+', Purpose => '*', Protection => 'public', Virtual => undef, Returns => 'void', Class => $class, Method => "~$class()", Arguments => [ "$class *" ], SigSlot => undef, Const => undef, Code => undef }, 'PerlQt::Method'; push @{$Input{$class}{'Proto'}}, $destroy; push @{$Methods{$class}{'DESTROY'}}, $destroy; $Prototypes{$class}{&polyname($destroy)} = $destroy; } $Class = $ssalc; close PIG; } sub readmodule { my $class = shift; veryverbose "Reading $class..."; # $Module = $class; loadmodule($class); $LinkList{$class}++; if($Info{$class}{'Inherit'}) { for my $super ($Info{$class}->inherit) { next if $Info{$super}; veryverbose "\n"; readmodule($super, $class); } } veryverbose "\n" unless shift; } sub startsource ($) { my $class = shift; my $info = $Info{$class}; open SOURCE, ">$Sourcedir/pig_$class.c"; # manifest "pig_" . $class . ".c"; makefile "pig_" . $class; open HEADER, ">$Sourcedir/pig_$class.h"; # manifest "pig_" . $class . ".h"; my $ifndef = uc("pig_${class}_h"); header "#ifndef $ifndef\n"; header "#define $ifndef\n\n"; for my $include (@Include) { for my $undef ($Info{$class}->undefine) { header "#undef $undef\n"; } for my $define ($Info{$class}->define) { if($define =~ /^(\w+)=(.*)$/) { header "#undef $1\n"; header "#define $1 $2\n"; } else { header "#undef $define\n"; header "#define $define\n"; } } header qq'#include "$include"\n'; } for my $include ($info->include) { for my $undef ($Info{$class}->undefine) { header "#undef $undef\n"; } for my $define ($Info{$class}->define) { if($define =~ /^(\w+)=(.*)$/) { header "#undef $1\n"; header "#define $1 $2\n"; } else { header "#undef $define\n"; header "#define $define\n"; } } header qq'#include <$include>\n'; } if($info->virtual) { open VHEADER, ">$Sourcedir/pig_${class}_v.h"; # manifest "pig_" . $class . "_v.h"; header qq'#include "pig_${class}_v.h"\n'; $ifndef = uc("pig_${class}_v_h"); vheader "#ifndef $ifndef\n"; vheader "#define $ifndef\n\n"; if($info->virtual > 1) { for my $super ($info->virtual) { vheader qq'#include "pig_${super}_v.h"\n' if $super ne $class; } vheader "\n"; } else { vheader qq'#include "$VirtualHeader"\n\n' if $info->virtual == 1; } } header "\n"; source "#define " . uc("pig_${class}_c") . "\n"; source qq'#include "pig_$class.h"\n'; my %inclist; for my $proto (@{$Input{$class}{'Proto'}}) { next unless $proto->everylang; my $ret = $proto->{'Returns'}; next if $ret eq 'void'; unless(exists $IncCache{$ret}) { my $type = fetch_ret($ret, 1); if($type =~ /^new (\w+)$/) { $IncCache{$ret} = $1; } else { $IncCache{$ret} = 0; } } if($IncCache{$ret} && $IncCache{$ret} ne $class) { for my $header ($Info{$IncCache{$ret}}->include) { $inclist{$header}++; } } } for my $include (sort keys %inclist) { source qq'#include "$include"\n'; } source "\n"; } sub endsource ($) { my $class = shift; my $info = $Info{$class}; my $ifndef = uc("pig_${class}_h"); header "#endif // $ifndef\n"; if($info->virtual) { $ifndef = uc("pig_${class}_v_h"); vheader "#endif // $ifndef\n"; close VHEADER; } close HEADER; close SOURCE; } sub startiheader { open(IHEADER, ">$Sourcedir/pig_import_${Module}.h") || die; iheader qq'#include "pig.h"\n'; iheader qq'#include "pigtype.h"\n\n'; } sub endiheader { iheader "\nPIG_IMPORT_TABLE(PIG_${Module})\n"; # iheader "struct pig_symboltable PIG_import_${Module}\[] = {\n"; for my $import (@Vtbl, @VtblIn) { iheader " PIG_IMPORT_SUBTABLE(PIG_${import}_vtbl)\n"; # iheader " { 0, (void *)PIG_${import}_import_vtbl },\n"; } iheader "PIG_IMPORT_ENDTABLE\n\n"; # iheader " { 0, 0 }\n};\n"; close(IHEADER); } sub writeheader { my $start = "struct pig_alias_$Class : $Class {\n"; return if exists $Info{$Class}{'Class'}; for my $proto (@{$Methods{$Class}{'new'}}) { next if $proto->perlonly; my $decl = cpp_constructor_decl($proto); next unless $decl; # BUG header $start; $start = ""; header " pig_alias_$decl {}\n"; } for my $proto (sort { $a->{'Method'} cmp $b->{'Method'} } values %{$Prototypes{$Class}}) { next unless $proto->protected; # || $proto->virtual next if $proto->constructor || $proto->destructor || $proto->abstract || $proto->private || $proto->variable || $proto->{'Code'}; header $start; $start = ""; my $decl = cpp_decl_proto($proto, 'pig'); $decl =~ s/(\w+\()/pig_alias_$1/; header " "; header "static " if $proto->static; header "$decl { "; header "return " if $proto->{'Returns'} ne 'void'; header "$Class\::$proto->{'Method'}\(" . cpp_argname_list($proto, 'pig') . "\);"; header " }\n"; } header "};\n\n" unless $start; } sub by_protection { # SLOW!!! my $x = ''; $x = "A" if $a->public; $x = "B" if $a->protected; $x = "C" if $a->private; $x .= $a->{'Method'}; my $y = ''; $y = "A" if $b->public; $y = "B" if $b->protected; $y = "C" if $b->private; $y .= $b->{'Method'}; $x cmp $y; } sub write_virtual_methods_def { vheader "#define pig_virtual_${Class}_methods"; for my $super (info->virtual) { vheader " \\\n pig_virtual_${super}_methods" if $super ne $Class; } my $prot = ''; for my $proto (sort by_protection values %Exclusive) { next unless $proto->virtual; next if $proto->destructor; if($prot ne $proto->{'Protection'}) { $prot = $proto->{'Protection'}; vheader " \\\n"; vheader "$prot:"; } my $decl = cpp_decl_proto($proto); # BUG: Can be removed vheader " \\\n virtual $decl;" if $decl; } vheader "\n\n"; } sub write_virtual_class { my $header = shift; my @vlist; vheader "extern pigfptr _pig_virtual_$Class\[];\n\n"; vheader "struct pig_virtual_$Class : "; if(info->virtual > 1) { my $i = 0; for my $super (info->virtual) { next if $super eq $Class; vheader ", " if $i++; vheader "pig_virtual_$super"; } } else { vheader "virtual pig_virtual"; } vheader " {\n"; my $idx = 0; for my $poly (sort keys %Exclusive) { my $proto = $Exclusive{$poly}; next unless $proto->virtual; next if $proto->destructor; push @vlist, $proto; my $decl = cpp_decl_proto($proto, "pig"); my $ptr = cpp_call_fptr($proto, "pig", "_pig_virtual_${Class}\[$idx]", "const pig_virtual"); $decl =~ s/(\w+\()/pig_virtual_$1/; vheader " $decl {\n"; vheader "\t"; vheader "return " if $proto->{'Returns'} ne 'void'; vheader $ptr; vheader ";\n }\n"; $idx++; } vheader "};\n\n"; source "PIG_EXPORT_TABLE(PIG_${Class}_vtbl)\n" unless $header; # source "struct pig_symboltable PIG_${Class}_export_vtbl[] = {\n"; export "PIG_DECLARE_EXPORT_TABLE(PIG_${Class}_vtbl)\n" unless $header; # export "extern struct pig_symboltable PIG_${Class}_export_vtbl[];\n"; $idx++ unless $idx; push @Vtbl, $Class unless $header; push @VtblIn, $Class if $header; iheader "pigfptr _pig_virtual_$Class\[$idx];\n"; $idx = 0; iheader "PIG_IMPORT_TABLE(PIG_${Class}_vtbl)\n"; # iheader "struct pig_symboltable PIG_${Class}_import_vtbl[] = {\n"; for my $proto (@vlist) { my $decl; $decl .= cpp_type($proto->{'Returns'}) . " (*)(const pig_virtual *"; my $x = 0; for my $arg (@{$proto->{'Arguments'}}) { next unless $x++ || $proto->static; $decl .= ", "; $decl .= cpp_type($arg); } $decl .= ")"; my $poly = polyname($proto); source qq~ PIG_EXPORT_VIRTUAL("$Class\::$poly", ($decl)pig_virtual_${Class}__$proto->{'Name'})\n~ unless $header; # source qq~ { "virtual $Class\::$poly", (void *)($decl)pig_virtual_${Class}__$proto->{'Name'} },\n~; iheader qq~ PIG_IMPORT_VIRTUAL("$Class\::$poly", &_pig_virtual_${Class}\[$idx])\n~; # iheader qq~ { "virtual $Class\::$poly", (void *)&_pig_virtual_${Class}\[$idx] },\n~; $idx++; } source "PIG_EXPORT_ENDTABLE\n" unless $header; # source " { 0, 0 }\n};\n"; iheader "PIG_IMPORT_ENDTABLE\n\n"; # iheader " { 0, 0 }\n};\n\n"; # for my $proto (@vlist) { # } } sub cpp_deftype { my $arg = shift; return ($arg =~ /^.*=\s*(.*)/) ? $1 : undef; } sub cpp_defaultarg { my $arg = shift; my $def = cpp_deftype($arg); # print "$def\n"; return defined($def) ? " = $def" : ""; } sub cpp_decl_proto { my $proto = shift; my $pre = shift; my $nodefault = shift; my $decl; unless($proto->constructor || $proto->destructor) { $decl .= cpp_type($proto->{'Returns'}) . " "; } $decl .= "$proto->{'Method'}("; my $x = 0; for my $arg (@{$proto->{'Arguments'}}) { next unless $x++ || $proto->static; $decl .= ", " if ($x - (1 - $proto->static)) > 1; if($arg eq '...') { $decl .= $arg; next; } my $type = cpp_type($arg); return '' unless $type; # BUG! $decl .= $type; $decl .= " $pre" . ($x-1) if $pre; # print "$arg\n"; $decl .= cpp_defaultarg($arg) unless $nodefault; } $decl .= ")"; if($proto->const) { $decl .= " const"; } return $decl; } sub cpp_call_fptr { my($proto, $pre, $ptr, $class) = @_; my $call; $call = "(*(" . cpp_type($proto->{'Returns'}) . " ("; # $call .= "$class\::" if $class; $call .= "*)("; my $x = 0; $call .= "$class *" if $class; for my $arg (@{$proto->{'Arguments'}}) { next unless $x++ || $proto->static; $call .= ", " if $class || ($x - (1 - $proto->static)) > 1; my $type = cpp_type($arg); $call .= $type; } $call .= "))$ptr)("; $call .= "this" if $class; $x = 0; for my $arg (@{$proto->{'Arguments'}}) { next unless $x++ || $proto->static; $call .= ", " if $class || ($x - (1 - $proto->static)) > 1; $call .= "$pre" . ($x-1); } $call .= ")"; return $call; } sub cpp_argname_list { my $proto = shift; my $pre = shift; my $arglist = ''; my $x = 0; for my $arg (@{$proto->{'Arguments'}}) { next unless $x++ || $proto->static; next if $arg eq '...'; $arglist .= ", " if ($x - (1 - $proto->static)) > 1; $arglist .= $pre . ($x-1); } return $arglist } sub cpp_constructor_decl { my $proto = shift; my @ret; my $s = ''; $s .= cpp_decl_proto($proto, 'pig'); return $s unless $s; # BUG! $s .= " : $Class("; $s .= cpp_argname_list($proto, 'pig'); $s .= ")"; return $s; } sub write_enhanced_class { vheader "class pig_enhanced_$Class : public $Class, private pig_virtual_$Class {\n"; vheader " pig_virtual_${Class}_methods\n"; vheader "public:\n"; for my $proto (@{$Methods{$Class}{'new'}}) { next if $proto->perlonly; vheader " pig_enhanced_" . cpp_constructor_decl($proto) . ", pig_virtual((void *)this) {}\n"; } vheader " virtual ~pig_enhanced_$Class();\n"; vheader "};\n\n"; } sub writevheader { write_virtual_methods_def; write_virtual_class; write_enhanced_class; } sub fetch_varg { my $argument = shift; my $argname = shift; my $arg = cpp_type($argument); my $def = cpp_deftype($argument); my $cast = cpp_cast($argument); my $type = pig_type($argument); $type =~ s/^\s*//; $type =~ s/\s*$//; $type =~ s/\s*([\*\&])/$1/g; my $s = ''; my $cmp = $arg; $cmp =~ s/\s*([\*\&])/$1/g; if(exists $Types{$type}) { my $c = ''; if(exists $Cast{$Types{$type}} && $Cast{$Types{$type}} =~ /\(.*\)/) { $c = $Cast{$Types{$type}}; } my $pre = ""; my $xtype = $Types{$type}; if($xtype =~ s/(\W).*//) { $pre = '&' if $1 eq '&'; } $s .= "pig_type_${xtype}_push(${pre}${c}$argname)"; } elsif($cmp ne $type) { if($type =~ /^(\w+)\s*\*$/) { $s = "pig_type_${1}_push($argname)"; } else { $type =~ s/\W.*//; $s = "pig_type_${type}_push($argname)"; } } elsif($cast =~ /^(?:const\s+)?(\w+)/) { my $class = $1; loadmodule($class); if($cast =~ /^const\s+(\w+)\s*\*$/) { $s = qq'pig_type_const_object_push($argname, "$1")'; } elsif($cast =~ /^const\s+(\w+)\s*\&$/) { $s = qq'pig_type_const_object_ref_push(&$argname, "$1")'; } elsif($cast =~ /^(\w+)\s*\*$/) { $s = qq'pig_type_object_push($argname, "$1")'; } elsif($cast =~ /^(\w+)\s*\&$/) { $s = qq'pig_type_object_ref_push(&$argname, "$1")'; } elsif($cast =~ /^(\w+)$/) { $s = qq'pig_type_object_push(&$argname, "$1")'; } else { print "NO $argument\n"; } } else { print "--$argument\n"; $s = "($arg)pig_argument_skip()"; } return $s; } sub fetch_vret { my $argument = shift; my $arg = cpp_type($argument); my $cast = cpp_cast($argument); my $type = pig_type($argument); $type =~ s/^\s*//; $type =~ s/\s*$//; $type =~ s/\s*([\*\&])/$1/g; my $s = ''; my $cmp = $arg; $cmp =~ s/\s*([\*\&])/$1/g; if(exists $Types{$type}) { my $t = ''; if(exists $Cast{$Types{$type}}) { $t = "($arg)"; } my $pre = ""; my $xtype = $Types{$type}; if($xtype =~ s/(\W).*//) { $pre = '&' if $1 eq '&'; my $xarg = $arg; $xarg =~ s/\&.*//; $t = "*($xarg *)"; } $s = "${t}pig_type_${xtype}_pop()"; # } elsif($cmp ne $type) { # $s = "pig_type_${type}_pop()"; } else { if($argument =~ /^(?:const\s+)?(\w+)/) { my $class = $1; loadmodule($class); if($argument =~ /^const\s+(\w+)\s*\*$/) { $s = qq'(const $1 *)pig_type_const_object_pop("$1")'; } elsif($argument =~ /^const\s+(\w+)\s*\&?$/) { $s = qq'*(const $1 *)pig_type_const_object_ref_pop("$1")'; } elsif($argument =~ /^(\w+)\s*\*$/) { $s = qq'($1 *)pig_type_object_pop("$1")'; } elsif($argument =~ /^(\w+)\s*\&?$/) { $s = qq'*($1 *)pig_type_object_ref_pop("$1")'; } elsif($argument =~ /^(\w+)$/) { $s = qq'*($1 *)pig_type_object_ref_pop("$1")'; } else { print "NO $argument\n"; } } else { die "We must all die from $argument\n"; } # print "%$argument\n"; } return $s; } sub write_virtual_methods { for my $poly (sort keys %Exclusive) { my $proto = $Exclusive{$poly}; next unless $proto->virtual; next if $proto->destructor; local($proto->{'Const'}) = ""; # Beware!!! my $decl = cpp_decl_proto($proto, 'pig', 1); # $decl =~ s/(\w+\()/pig_virtual_$Class\::pig_virtual_$1/; $decl =~ s/(\w+)\(([^\)])/pig_virtual_${Class}__$1(const pig_virtual *pig0, $2/; $decl =~ s/(\w+)\(\)/pig_virtual_${Class}__$1(const pig_virtual *pig0)/; unless($proto->everylang) { source "$decl;\n\n"; next; } source "static $decl {\n"; source " PIG_VIRTUAL(PIG_$proto->{'Class'}_$proto->{'Name'});\n"; my $x = 0; for my $arg (@{$proto->{'Arguments'}}) { next unless $x++; source " " . fetch_varg($arg, "pig" . ($x-1)) . ";\n"; # source " pig_push(&pig" . ($x-1) . ");\n"; } if($proto->{'Returns'} ne 'void') { source " pig_call_retmethod(pig0, \"$proto->{'Name'}\");\n"; source " return(" . fetch_vret($proto->{'Returns'}) . ");\n"; } else { source " pig_call_method(pig0, \"$proto->{'Name'}\");\n"; } source "}\n\n"; } for my $poly (sort keys %Inclusive) { my $proto = $Inclusive{$poly}; next unless $proto->virtual; next if $proto->destructor; my $decl = cpp_decl_proto($proto, 'pig', 1); $decl =~ s/(\w+\()/pig_enhanced_$Class\::$1/; source "$decl {\n"; source " "; source "return " if $proto->{'Returns'} ne 'void'; source "pig_virtual_$proto->{'Method'}(" . cpp_argname_list($proto, 'pig') . ");\n"; source "}\n\n"; } } sub newfirst { my($x, $y) = ($a, $b); for my $z ($x, $y) { $z = "A" if $z eq "new"; # highest alpha string $z = "AA" if $z eq "DESTROY"; # next highest alpha string } return $x cmp $y; } sub i { my $in = ''; $in .= ("\t" x ($Indent/2)); $in .= " " if $Indent % 2; return $in; } sub pig_type { my $argument = shift; if($argument =~ /\{\@?(.*?)\}/) { return $1; } else { return cpp_type($argument); } } sub cpp_cast { my $arg = pig_type(@_); my $targ = cpp_type(@_); $arg =~ s/^\s*//; $arg =~ s/\s*$//; $arg =~ s/\s*([\*\&])/$1/g; if(exists $Cast{$arg}) { my $cast = $Cast{$arg}; $cast =~ s/^\(*//; $cast =~ s/\)*$//; return $cast; } # return (exists $Cast{$arg}) ? $Cast{$arg} : $targ; return $targ; } sub fetch_ret { my $argument = shift; my $arg = cpp_type($argument); my $cast = cpp_cast($argument); my $type = pig_type($argument); $type =~ s/^\s*//; $type =~ s/\s*$//; $type =~ s/\s*([\*\&])/$1/g; my $s = ''; my $cmp = $arg; $cmp =~ s/\s*([\*\&])/$1/g; my $ex = ""; if($argument =~ /\{\s*(\w+)\s*\((.*)\)\}/) { my $list = $2; my @args; for my $x (split /,\s*/, $list) { $x =~ s/\$[(\d)]/pig$1/g; $x =~ s/\$this/pig0/g; push @args, $x; } $ex = ", " . join(", ", @args) if @args; } if(exists $Types{$type}) { my $c = ''; if(exists $Cast{$Types{$type}} && $Cast{$Types{$type}} =~ /\(.*\)/) { $c = $Cast{$Types{$type}}; } # $c = "($Cast{$Types{$type}})" if exists $Cast{$Types{$type}}; # $c =~ s/\(+/(/g; # $c =~ s/\)+/)/g; my $pre = ""; my $xtype = $Types{$type}; if($xtype =~ s/(\W).*//) { $pre = '&' if $1 eq '&'; } $s = "pig_type_${xtype}_return(${pre}${c}pigr$ex)"; # $s =~ s/\$type/$arg/g; } elsif($cmp ne $type) { # print "?$type\n"; my $pre = ""; if($type =~ s/(\W).*//) { $pre = '&' if $1 eq '&'; } $s = "pig_type_${type}_return(${pre}pigr$ex)"; } else { if($argument =~ /^(?:const\s+)?(\w+)/) { my $class = $1; # print "Loading $class\n"; loadmodule($class); if($Info{$class}->copy) { return "new $class" if shift; # include headers for new $class() if($argument =~ /^(?:const\s+)?(\w+)\s*\*$/) { $s = qq{pig_type_new_object_return(pigr ? new $1(*pigr) : (void *)pigr, "$1")}; } elsif($argument =~ /^(?:const\s+)?(\w+)\s*\&?$/) { $s = qq{pig_type_new_object_return(new $1(pigr), "$1")}; } else { print "NO $argument\n"; } } else { if($argument =~ /^const\s+(\w+)\s*\*$/) { $s = qq{pig_type_const_object_return(pigr, "$1")}; } elsif($argument =~ /^const\s+(\w+)\s*\&$/) { $s = qq{pig_type_const_object_return(&pigr, "$1")}; } elsif($argument =~ /^(\w+)\s*\*$/) { $s = qq{pig_type_object_return(pigr, "$1")}; } elsif($argument =~ /^(\w+)\s*\&?$/) { $s = qq{pig_type_object_return(&pigr, "$1")}; } else { print "NO $argument\n"; } } } else { die "We must all die from $argument\n"; } # print "%$argument\n"; } return $s; } sub fetch_arg { my $argument = shift; my $idx = shift; my $prefix = shift || 'pig_type_'; my $arg = cpp_type($argument); my $def = cpp_deftype($argument); my $cast = cpp_cast($argument); my $defarg = defined($def) ? "($cast)($def)" : ""; my $type = pig_type($argument); $type =~ s/^\s*//; $type =~ s/\s*$//; $type =~ s/\s*([\*\&])/$1/g; my $s = ''; my $cmp = $arg; $cmp =~ s/\s*([\*\&])/$1/g; # warn "$type\n"; if(exists $Types{$type}) { my $c = ''; my $t = ''; if(exists $Cast{$Types{$type}}) { if($Cast{$Types{$type}} =~ /\(.*\)/) { $c = "$Cast{$Types{$type}}"; $t = "($arg)"; } elsif($defarg) { $defarg = $def; } } my $pre = ""; my $xtype = $Types{$type}; if($xtype =~ s/(\W).*//) { $pre = '&' if $1 eq '&'; my $xarg = $arg; $xarg =~ s/\s*\&//g; $t = "*($xarg *)" } if($defarg) { $s .= "$t$prefix${xtype}_defargument(${pre}${c}$defarg)"; } else { $s .= "$t$prefix${xtype}_argument()"; } } elsif($cmp ne $type) { if($type =~ /^(\w+)\s*\*$/) { $s = "*($arg *)$prefix${1}_argument($defarg)"; } else { my $xarg = ""; my $commaxarg = ""; if($type =~ /\((.+)\)/) { $xarg = $1; $commaxarg = ", $xarg"; } $type =~ s/\W.*//; if($arg =~ /\&\s*$/) { if($defarg) { $s = "$prefix${type}_defargument($defarg$commaxarg)"; } else { $s = "$prefix${type}_argument($xarg)"; } } else { if($defarg) { $s = "($arg)$prefix${type}_defargument($defarg$commaxarg)"; } else { $s = "($arg)$prefix${type}_argument($xarg)"; } # $s = "($arg)$prefix${type}_argument($defarg)"; } } } elsif($cast =~ /^(?:const\s+)?(\w+)/) { my $class = $1; loadmodule($class); if($cast =~ /^const\s+(\w+)\s*\*$/) { if(defined($def)) { $s .= qq'(const $1 *)${prefix}const_object_defargument($def, "$1")'; } else { $s .= qq'(const $1 *)${prefix}const_object_argument("$1")'; } } elsif($cast =~ /^const\s+(\w+)\s*\&$/) { if(defined($def)) { $s .= qq'*(const $1 *)${prefix}const_object_ref_defargument(&pig_$idx, "$1")'; } else { $s .= qq'*(const $1 *)${prefix}const_object_ref_argument("$1")'; } } elsif($cast =~ /^(\w+)\s*\*$/) { if(defined($def)) { $s .= qq'($1 *)${prefix}object_defargument($def, "$1")'; } else { $s .= qq'($1 *)${prefix}object_argument("$1")'; } } elsif($cast =~ /^(\w+)\s*\&?$/) { if(defined($def)) { $s .= qq'*($1 *)${prefix}object_ref_defargument(&pig_$idx, "$1")'; } else { $s .= qq'*($1 *)${prefix}object_ref_argument("$1")'; } } else { print "NO $argument\n"; } } else { print "-$argument\n"; $s = "($arg)pig_argument_skip()"; } return $s; } sub write_proto_method { my $proto = shift; my $x = 0; return if $proto->{'Name'} eq 'newcopy'; # broken for now if($proto->destructor) { source i."$Class * pig0 = ($Class *)pig_type_object_destructor_argument(\"$Class\");\n"; } else { for my $argument (@{$proto->{'Arguments'}}) { my $arg = cpp_type($argument); if(cpp_deftype($argument) && $arg =~ /\&/) { source i."$arg pig_$x = ".cpp_deftype($argument).";\n"; } source i.$arg; source " pig$x"; source " = "; if($x == 0 && !$proto->static && !$proto->constructor) { source fetch_arg($argument, $x, 'pig_type_this_'); } else { source fetch_arg($argument, $x); } source ";\n"; $x++; } } source i."PIG_END_ARGUMENTS;\n\n"; # source "\n" if @{$proto->{'Arguments'}}; if($proto->abstract) { source i."pig_call_abstract(\"$Class\::$proto->{Name}\");\n\n"; } elsif($proto->{'Returns'} ne 'void') { my $arg = cpp_type($proto->{'Returns'}); source i."$arg pigr = "; } if($proto->{'Code'}) { my $code = $proto->{'Code'}; $code =~ s/^\s*//; $code =~ s/\s*//; $code =~ s/\$class/pigclass/g; $code =~ s/\$this/pig0/g; $code =~ s/\$(\d+)/pig$1/g; source "$code;\n\n"; } elsif($proto->variable) { my $code = $proto->{'Name'}; my $set = 0; if($code =~ /^set/) { $set = 1; $code =~ s/^set([A-Z])/\l$1/; $code =~ s/^set([a-z])/\u$1/; } if($set) { source "pig0->$code = pig1;\n\n"; } else { source "pig0->$code;\n\n"; } } elsif($proto->destructor) { if(info->virtual) { source "if(pig_object_can_delete()) delete ((pig_enhanced_$Class *)pig0);\n\n"; } else { source "if(pig_object_can_delete()) delete pig0;\n\n"; } source i."pig_return_nothing();\n"; return; } elsif($proto->constructor) { if(info->virtual) { source "new pig_enhanced_"; } else { source "new "; } } elsif($proto->static) { if($proto->protected) { source "pig_alias_$Class\::pig_alias_"; } else { source "$Class\::"; } } else { if($proto->abstract) { } elsif($proto->protected) { source "((pig_alias_$Class *)pig0)->pig_alias_"; } elsif($proto->virtual) { source "pig0->$Class\::"; } else { source "pig0->"; } } unless($proto->{'Code'} || $proto->variable || $proto->abstract) { source "$proto->{'Method'}("; source cpp_argname_list($proto, 'pig'); source ");\n\n"; } if($proto->abstract) { } elsif($proto->{'Name'} eq 'new') { source i.qq'pig_type_new_castobject_return(pigr, "$Class", pigclass);\n'; } elsif($proto->{'Returns'} ne 'void') { source i.fetch_ret($proto->{'Returns'}).";\n"; } else { source i."pig_return_nothing();\n"; } } sub group_of_type { my $item = shift; my $arg = cpp_type($item); my $type = pig_type($item); my $cmp = $arg; $cmp =~ s/\s*([\*\&])/$1/g; $cmp =~ s/\s*\*.*//; # $type = $Types{$type} if exists $Types{$type} && $Types{$type} ne $cmp; $type =~ s/^\s*//; $type =~ s/\s*$//; $type =~ s/\s*([\*\&])/$1/g; $type =~ s/\s*\*.*//; if($type =~ /^(?:int|long|uint|short|enum)$/) { return 'int'; } elsif($type =~ /^bool$/) { return 'bool'; } elsif($type =~ /^(?:float|double)$/) { return 'float'; } elsif($arg =~ /^(?:const\s+)?char\s*\*\s*$/) { return 'string'; } elsif($cmp ne $type) { return group_of_type($arg); } elsif($arg =~ /^(?:const\s+)?([\w:]+)/ and ismod $1) { return 'class'; } else { $arg =~ s/\s+/ /g; $arg =~ s/\s*([\*\&])/$1/g; # print "okay, $arg => $Types{$arg}\n"; if(exists $Types{$arg} && $Types{$arg} ne $arg) { # print "Getting $Types{$arg} from $arg\n"; return group_of_type($Types{$arg}); } # print "Okay, casting $arg to $Cast{$arg}\n"; if(exists $Cast{$arg} && $Cast{$arg} ne $arg) { return group_of_type($Cast{$arg}); } # print "UNKNOWN '$type' '$arg' '$cmp'\n"; # if(exists $Types{$arg} return 'unknown'; } } sub branched_filter { my $info = shift; my $list = shift; my $ninfo = {}; $ninfo->{'undef'} = [ map { $$list{$_} ? ($_) : () } @{$info->{'undef'}} ]; $ninfo->{'string'} = [ map { $$list{$_} ? ($_) : () } @{$info->{'string'}} ]; $ninfo->{'mystery'} = [ map { $$list{$_} ? ($_) : () } @{$info->{'mystery'}} ]; for my $key (keys %{$info->{'number'}}) { $ninfo->{'number'}{$key} = [ map { $$list{$_} ? ($_) : () } @{$info->{'number'}{$key}} ]; } for my $key (keys %{$info->{'class'}}) { $ninfo->{'class'}{$key} = [ map { $$list{$_} ? ($_) : () } @{$info->{'class'}{$key}} ]; } return $ninfo; } sub branch_condition { my $pm = shift; my $idx = shift; my $list = shift; my %list; if($Method eq 'new') { return 0 unless $idx < @$pm; } else { return 0 unless $idx < $#$pm; } for my $item (@$list) { $list{$item}++ } source "{\n"; $Indent++; branching_conditional($pm, $idx + 1, \%list); # mutual recursion $Indent--; source i."}\n"; return 1; } sub byinheritance { my(@asuper, @bsuper); supernames($a, \@asuper); supernames($b, \@bsuper); if(grep($a, @bsuper)) { return 1; } elsif(grep($b, @asuper)) { return 0; } else { return $a cmp $b; } } sub branching_conditional { my $pm = shift; my $idx = shift; my $list = shift; my $info = branched_filter(($Method eq 'new') ? $pm->[$idx-1] : $pm->[$idx], $list); my $else = 0; source i."unsigned int pigi$idx = pig_argument_info($idx);\n"; if(scalar @{$info->{'string'}} && scalar @{$info->{'string'}} != scalar @{$info->{'undef'}}) { source i; source "else " if $else++; source "if(pig_is_string($idx)) "; if(scalar @{$info->{'string'}} == 1) { source "pigs = $info->{'string'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'string'})) { source "pigs = 0; // AMBIGUOUS\n"; } } if(scalar keys %{$info->{'number'}}) { my $c = scalar keys %{$info->{'number'}}; if($c == 1) { my($key) = keys(%{$info->{'number'}}); source i; source "else " if $else++; source "if(pig_is_number($idx)) "; if(scalar @{$info->{'number'}{$key}} == 1) { source "pigs = $info->{'number'}{$key}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'number'}{$key})) { source "pigs = 0; // AMBIGUOUS\n"; } } elsif($c == 2) { my($k1, $k2) = keys(%{$info->{'number'}}); if($k1 eq 'int') { source i; source "else " if $else++; source "if(pig_is_int($idx)) "; if(scalar @{$info->{'number'}{'int'}} == 1) { source "pigs = $info->{'number'}{'int'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'number'}{'int'})) { source "pigs = 0; // AMBIGUOUS\n"; } } if($k2 eq 'int') { source i; source "else " if $else++; source "if(pig_is_int($idx)) "; if(scalar @{$info->{'number'}{'int'}} == 1) { source "pigs = $info->{'number'}{'int'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'number'}{'int'})) { source "pigs = 0; // AMBIGUOUS\n"; } } if($k1 eq 'float') { source i; source "else " if $else++; source "if(pig_is_float($idx)) "; if(scalar @{$info->{'number'}{'float'}} == 1) { source "pigs = $info->{'number'}{'float'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'number'}{'float'})) { source "pigs = 0; // AMBIGUOUS\n"; } } if($k2 eq 'float') { source i; source "else " if $else++; source "if(pig_is_float($idx)) "; if(scalar @{$info->{'number'}{'float'}} == 1) { source "pigs = $info->{'number'}{'float'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'number'}{'float'})) { source "pigs = 0; // AMBIGUOUS\n"; } } if($k1 eq 'bool') { source i; source "else " if $else++; source "if(pig_is_bool($idx)) "; if(scalar @{$info->{'number'}{'bool'}} == 1) { source "pigs = $info->{'number'}{'bool'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'number'}{'bool'})) { source "pigs = 0; // AMBIGUOUS\n"; } } if($k2 eq 'bool') { source i; source "else " if $else++; source "if(pig_is_bool($idx)) "; if(scalar @{$info->{'number'}{'bool'}} == 1) { source "pigs = $info->{'number'}{'bool'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'number'}{'bool'})) { source "pigs = 0; // AMBIGUOUS\n"; } } } } if(scalar @{$info->{'undef'}}) { if(scalar @{$info->{'string'}} == scalar @{$info->{'undef'}}) { source i; source "else " if $else++; source "if(pig_is_string($idx) || pig_is_undef($idx)) "; if(scalar @{$info->{'string'}} == 1) { source "pigs = $info->{'string'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'string'})) { source "pigs = 0; // AMBIGUOUS\n"; } } elsif(scalar(keys %{$info->{'class'}}) == 1 && scalar(@{$info->{'class'}{(keys %{$info->{'class'}})[0]}}) == (scalar(@{$info->{'undef'}}) - scalar(@{$info->{'string'}}))) { my $key = (keys %{$info->{'class'}})[0]; source i; source "else " if $else++; source "if(pig_is_object($idx) || pig_is_undef($idx)) "; if(scalar @{$info->{'class'}{$key}} == 1) { source "pigs = $info->{'class'}{$key}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'class'}{$key})) { source "pigs = 0; // AMBIGUOUS\n"; } } else { source i; source "else " if $else++; source "if(pig_is_undef($idx)) "; if(scalar @{$info->{'undef'}} == 1) { source "pigs = $info->{'undef'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'undef'})) { source "pigs = 0; // AMBIGUOUS\n"; } } } if(scalar keys %{$info->{'class'}}) { if(scalar keys %{$info->{'class'}} == 1 && # (scalar(@{$info->{'string'}}) != scalar(@{$info->{'undef'}})) && scalar(@{$info->{'class'}{(keys %{$info->{'class'}})[0]}}) != (scalar(@{$info->{'undef'}}) - scalar(@{$info->{'string'}}))) { my($key) = keys(%{$info->{'class'}}); source i; source "else " if $else++; source "if(pig_is_object($idx)) "; if(scalar @{$info->{'class'}{$key}} == 1) { source "pigs = $info->{'class'}{$key}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'class'}{$key})) { source "pigs = 0; // AMBIGUOUS\n"; } } elsif(scalar(keys %{$info->{'class'}}) > 1) { my(@classes) = sort byinheritance keys %{$info->{'class'}}; for my $key (@classes) { source i; source "else " if $else++; source "if(pig_is_class($idx, \"$key\")) "; if(scalar @{$info->{'class'}{$key}} == 1) { source "pigs = $info->{'class'}{$key}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'class'}{$key})) { source "pigs = 0; // AMBIGUOUS\n"; } } } } if(scalar @{$info->{'mystery'}}) { source i; source "else " if $else++; source "if(pig_is_mystery($idx)) "; if(scalar @{$info->{'mystery'}} == 1) { source "pigs = $info->{'mystery'}[0];\n"; } elsif(!branch_condition($pm, $idx, $info->{'mystery'})) { source "pigs = 0; // AMBIGUOUS\n"; } } if(!$else && $idx < $#$pm) { # branching_conditional($pm, $idx + 1, $list); } } sub write_whichproto { my $protos = shift; my $method = $protos->[0]{'Name'}; my @argcnt; my $adj = 0; # my $v = $protos->[0]{'Class'} eq 'QScrollBar'; $adj = 1 if $method eq 'new'; for(my $item = 0; $item < @$protos; $item++) { my $proto = $protos->[$item]; my @arguments; my $x = 0; for my $arg (@{$proto->{'Arguments'}}) { push @arguments, $arg unless $arg =~ /\{\s*\@/; # if($v) { print "x[$item] ($arg)\n" } } for my $arg (@arguments) { # if($v) { print "deftype $arg\n" if defined cpp_deftype($arg); # } last if defined cpp_deftype($arg); $x++; } for my $i ($x .. scalar(@arguments)) { push @{$argcnt[$i]}, $item; } } source i."if("; my($i, $bottom); for($i = 0; $i < @argcnt; $i++) { if(!defined($bottom) && $argcnt[$i]) { if($i == 0) { $bottom = 0; next; } $bottom = $i; source "pigc <= ".($i-1+$adj); } elsif(defined($bottom) && !$argcnt[$i]) { source " || " if $bottom++; source "pigc == " . ($i+$adj); } } source " || " if $bottom++; source "pigc >= " . ($i+$adj); source ") pigs = 0;\n"; for($i = 0; $i < @argcnt; $i++) { next unless ref $argcnt[$i]; if(scalar @{$argcnt[$i]} == 1) { my $case = $argcnt[$i][0] + 1; source i."else if(pigc == ".($i+$adj).") pigs = $case;\n"; } else { my @protomatrix; source i."else if(pigc == ".($i+$adj).") {\n"; $Indent++; source i."// ".scalar(@{$argcnt[$i]})." possibilities\n"; for my $idx (0..($i-1)) { $protomatrix[$idx] = { 'undef' => [], 'string' => [], 'number' => {}, 'class' => {}, 'mystery' => [] }; } my %x; for my $idx (@{$argcnt[$i]}) { source "\n"; source i."// idx: ".($idx+1)."\n"; my $x = 0; for my $arg (@{$protos->[$idx]{'Arguments'}}[0..($i-1)]) { next if $arg =~ /\{\s*\@/; my $info = $protomatrix[$x++]; my $type = group_of_type($arg); #print "got $type of $arg\n" if $v; source i."// $type\n"; $x{$idx+1}++; if($type eq 'int') { push @{$info->{'number'}{'int'}}, $idx+1; } elsif($type eq 'float') { push @{$info->{'number'}{'float'}}, $idx+1; } elsif($type eq 'bool') { push @{$info->{'number'}{'bool'}}, $idx+1; } elsif($type eq 'string') { push @{$info->{'undef'}}, $idx+1; push @{$info->{'string'}}, $idx+1; } elsif($type eq 'class') { push @{$info->{'undef'}}, $idx+1 unless $arg =~ /\&\s*$/; my $class = $arg; $class =~ s/^\s*(?:const\s+)?(\w+).*$/$1/; push @{$info->{'class'}{$class}}, $idx+1; } elsif($type eq 'unknown') { push @{$info->{'mystery'}}, $idx+1; } # } else { # $x{$idx+1}--; # } } } for my $idx (0..$#protomatrix) { local($") = ', '; my $pm = $protomatrix[$idx]; source i."// \$info[$idx] = {\n"; source i."// 'undef' => [@{$pm->{'undef'}}],\n"; source i."// 'string' => [@{$pm->{'string'}}],\n"; my $x = 0; source i."// 'number' => {"; for my $number (sort keys %{$pm->{'number'}}) { source ", " if $x++ > 0; source "'$number' => [@{$pm->{'number'}{$number}}]"; } source "},\n"; $x = 0; source i."// 'class' => {"; for my $class (sort keys %{$pm->{'class'}}) { source ", " if $x++ > 0; source "'$class' => [@{$pm->{'class'}{$class}}]"; } source "},\n"; source i."// 'mystery' => [@{$pm->{'mystery'}}]\n"; source i."// };\n"; } $Method = $protos->[0]{'Name'}; branching_conditional(\@protomatrix, ($Method eq 'new') ? 1 : 0, \%x); $Indent--; source i."}\n"; } } } sub write_perl_methods { my @methods; source "static PIG_PROTO(PIG_${Class}_continue) {\n"; source " PIG_BEGIN(PIG_${Class}_continue);\n"; source " pig_object_continue();\n"; source " PIG_END;\n"; source "}\n\n"; push @methods, 'continue'; source "static PIG_PROTO(PIG_${Class}_break) {\n"; source " PIG_BEGIN(PIG_${Class}_break);\n"; source " pig_object_break();\n"; source " PIG_END;\n"; source "}\n\n"; push @methods, 'break'; for my $meth (sort newfirst keys %{$Methods{$Class}}) { my @protos; for my $proto (@{$Methods{$Class}{$meth}}) { push @protos, $proto unless #$proto->variable || $proto->private || # $proto->{'Code'} || # !$proto->everylang; $proto->cpponly; } my $protocnt = scalar(@protos); next if $protocnt == 0; push @methods, $meth; if($Methods{$Class}{$meth}[0]->destructor && $Methods{$Class}{$meth}[0]->public) { source "static PIG_PROTO(PIG_${Class}_delete) {\n"; source " PIG_BEGIN(PIG_${Class}_delete);\n"; source " $Class * pig0 = ($Class *)pig_type_object_destructor_argument(\"$Class\");\n"; source " PIG_END_ARGUMENTS;\n\n"; source " delete pig0;\n\n"; source " pig_return_nothing();\n"; source " PIG_END;\n"; source "}\n\n"; push @methods, 'delete'; } my $polymorph = ($protocnt > 1); my $poly = 1; source "static PIG_PROTO(PIG_${Class}_$meth) {\n"; source " PIG_BEGIN(PIG_${Class}_$meth);\n"; if($meth eq 'new') { source " const char *pigclass = pig_type_cstring_argument();\n"; } if($polymorph) { source "\n"; source " int pigs = 0;\n"; source " int pigc = pig_argumentcount();\n\n"; $Indent = 1; write_whichproto(\@protos); source " switch(pigs) {\n"; } $Indent = $polymorph ? 3 : 1; for my $proto (@protos) { if($polymorph) { source " case $poly:\n\t{\n"; $poly++; } write_proto_method($proto); if($polymorph) { source "\t}\n"; source "\tbreak;\n"; } } if($polymorph) { source " default:\n"; source qq{\tpig_ambiguous("$Class", "$protos[0]{'Name'}");\n\tbreak;\n}; source " }\n"; } source " PIG_END;\n"; source "}\n\n"; } export "extern pig_method PIG_${Class}_methods[];\n"; source "pig_method PIG_${Class}_methods[] = {\n"; for my $meth (sort newfirst @methods) { source " { \"$meth\", PIG_PROTONAME(PIG_${Class}_$meth) },\n"; } source " { 0, 0 }\n"; source "};\n\n"; } sub write_isa { export "extern const char *PIG_${Class}_isa[];\n"; source "const char *PIG_${Class}_isa[] = { "; for my $super (info->inherit) { source qq{"$super", } if $Info{$super}->class } source "0 };\n\n"; } sub supernames { my $class = shift; my $array = shift; return if exists $Info{$class}{'Class'} && !$Info{$class}{'Class'}; push @$array, $class; return unless exists $Info{$class}{'Inherit'}; for my $super ($Info{$class}->inherit) { supernames($super, $array); } } sub write_typecast { my $direction = shift; export "extern void *PIG_${Class}_${direction}cast(const char *, void *);\n"; source "void *PIG_${Class}_${direction}cast(const char *pig0, void *pig1) {\n"; my @super; supernames($Class, \@super); push @super, "virtual" if info->virtual; source " const char *pig_super[] = { "; for my $super (@super) { source qq{"$super", }; } source "0 };\n\n"; source " if(!pig0) return pig1;\n"; source " switch(pig_find_in_array(pig0, pig_super)) {\n"; my $x = 0; for my $super (@super) { source "\tcase $x: return (void *)"; if($direction eq 'from') { if($super eq 'virtual') { source "($Class *)(pig_enhanced_$Class *)(((pig_virtual *)pig1)->pig_this);\n"; } else { source "($Class *)($super *)pig1;\n"; } } else { if($super eq 'virtual') { source "(pig_virtual *)(pig_virtual_$Class *)(pig_enhanced_$Class *)($Class *)"; } else { source "($super *)($Class *)"; } source "pig1;\n"; } $x++; } source "\tdefault: return 0;\n"; source " }\n"; source "}\n\n"; } sub write_constants { # for my $constant ($Info{$Class}->export) { # if($constant =~ /(\%|\@|\$|\&)(\w+)(.*)/) { # my($type, $name, $rest) = ($1, $2, $3); # my $cast = 'ulong'; # if($type eq '%') { # $cast = $1 if $rest =~ s/^{(.*?)}//; # export "extern pig_struct_constantdata PIG_${Class}_constant_$name\[];\n"; # source "pig_struct_constantdata PIG_${Class}_constant_$name\[] = {\n"; # # $ConstantList{"PIG_${Class}_constant_$name"} = { # Name => $name, # Type => 'HASH', # Cast => ($cast eq 'ulong') ? undef : $cast # }; # # for my $key (sort keys %{$Input{$Class}{$type}{$name}}) { # source " { \"$key\", (long)($cast)$Input{$Class}{$type}{$name}{$key} },\n"; # } # source " { 0, 0 }\n"; # source "};\n\n"; # } # } # } my $type; my $c = $Info{$Class}{'Constant'}; if(keys %$c) { my @int; my @object; my %list; for my $constant (keys %$c) { $type = $c->{$constant}{'Type'}; if($type eq 'enum') { push @int, $constant; } elsif($type eq 'int') { push @int, $constant; } elsif($type eq 'uint') { push @int, $constant; } else { push @object, $constant; # print "No $constant $type\n"; } } if(@int) { source "static struct pig_constant_int PIG_${Class}_const_int[] = {\n"; for my $constant (sort @int) { source qq~ { "$constant", (long)$Class\::$constant },\n~; } source " { 0, 0 }\n"; source "};\n\n"; $list{"PIG_${Class}_const_int"} = "PIG_CONSTANT_INT"; } if(@object) { source "struct pig_constant_object PIG_${Class}_const_object[] = {\n"; for my $constant (sort @object) { my $t = $c->{$constant}{'Type'}; my $n; my $v; if($t =~ /(.*\w)\s*\*\s*$/) { $v = "$Class\::$constant"; $n = $1; } else { if($t =~ /([\w:]+)/) { $n = $1; } else { $n = $t; } $t = "$t*"; $v = "&$Class\::$constant"; } unless($t =~ /^const\s+/) { $t = "const $t"; } source qq~ { "$constant", (void *)($t)$v, "$n" },\n~; } source " { 0, 0, 0 }\n"; source "};\n\n"; $list{"PIG_${Class}_const_object"} = "PIG_CONSTANT_OBJECT"; } source "struct pig_constant PIG_${Class}_const[] = {\n"; for my $clist (keys %list) { source " { (void *)$clist, $list{$clist} },\n"; } source " { 0, 0 }\n"; source "};\n\n"; export "extern pig_constant PIG_${Class}_const[];\n"; } $c = $Info{$Class}{'Global'}; if(keys %$c) { my @int; my @object; for my $constant (keys %$c) { $type = exists $Types{$c->{$constant}{'Type'}} ? $Types{$c->{$constant}{'Type'}} : $c->{$constant}{'Type'}; if($type eq 'enum') { push @int, $constant; } elsif($type eq 'int') { push @int, $constant; } elsif($type eq 'uint') { push @int, $constant; } else { push @object, $constant; # print "No $type $constant\n"; } } if(@int) { source "struct pig_constant_int PIG_${Class}_global_int[] = {\n"; for my $constant (sort @int) { source qq~ { "$constant", (long)$constant },\n~; } source " { 0, 0 }\n"; source "};\n\n"; export "extern pig_constant_int PIG_${Class}_global_int[];\n"; $ConstantList{"PIG_${Class}_global_int"} = "PIG_CONSTANT_INT"; } if(@object) { source "struct pig_constant_object PIG_${Class}_global_object[] = {\n"; for my $constant (sort @object) { my $t = $c->{$constant}{'Type'}; my $n; my $v; if($t =~ /(.*\w)\s*\*\s*$/) { $v = $constant; $n = $1; } else { if($t =~ /([\w:]+)/) { $n = $1; } else { $n = $t; } $t = "$t*"; $v = "&$constant"; } unless($t =~ /^const\s+/) { $t = "const $t"; } source qq~ { "$constant", (void *)($t)$v, "$n" },\n~; } source " { 0, 0, 0 }\n"; source "};\n\n"; export "extern pig_constant_object PIG_${Class}_global_object[];\n"; $ConstantList{"PIG_${Class}_global_object"} = "PIG_CONSTANT_OBJECT"; } } } sub write_virtual_destructor { source "pig_enhanced_$Class\::~pig_enhanced_$Class() {\n"; source " pig_object_destroy(this, (pig_virtual *)this);\n"; source "}\n\n"; } sub writesource { if(info->class) { write_isa; write_typecast('to'); write_typecast('from'); } write_constants; write_perl_methods; write_virtual_destructor if info->virtual; write_virtual_methods if info->virtual; } sub findvirtual { my $class = shift; for my $poly (keys %{$Prototypes{$class}}) { $Inclusive{$poly} = $Prototypes{$class}{$poly} unless exists $Inclusive{$poly}; } for my $super ($Info{$class}->virtual) { next if $super eq $class; findvirtual($super); } } sub getvirtual { %Inclusive = (); %Exclusive = (); for my $super (info->virtual) { next if $super eq $Class; findvirtual($super); } %Exclusive = %{$Prototypes{$Class}}; for my $poly (keys %Exclusive) { if(exists $Inclusive{$poly}) { delete $Exclusive{$poly}; } else { $Inclusive{$poly} = $Exclusive{$poly}; } } } sub writemodule { my $class = shift; verbose "Writing $class..."; getvirtual if info->virtual; startsource $class; writesource; writeheader; writevheader if info->virtual; endsource $class; delete $LinkList{$class}; push @ClassList, $class; say "."; verbose "\n"; } #sub main { # arguments(@ARGV); # #MODULE: # for my $module (@Modules) { # my $path = find $module; # next MODULE unless $path; # $Module = $module; # $Path = $path; # # say "Loading $module..."; # verbose "Loading $module..."; # ## mklibdir; # # my $srcdir = mksrcdir $module; # next MODULE unless $srcdir; # $Sourcedir = $srcdir; # ## next MODULE unless startmanifest; ## next MODULE unless startmakefile; # next MODULE unless startmodulecode; # startiheader; # # my(@classes) = list $path; # # verbose "\n"; # # for my $class (@classes) { # $Class = $class; # readmodule $class; # writemodule $class; # } # say "\n"; # # endiheader; # endmodulecode; ## endmanifest; endmakefile; # } #} sub GenerateSource { my(%args) = @_; for my $typemap (@{$args{'TYPEMAPS'}}) { readtypemap($typemap); } @Include = @{$args{'INCLUDE'}}; $Sourcedir = $args{'SOURCEDIR'}; $VirtualHeader = $args{'VIRTUALHEADER'}; @S = (@S, @{$args{'LINK'}}) if ref $args{'LINK'}; for my $module (@{$args{'DIR'}}) { $Module = $module; push @S, $module; $Module =~ s/\W+.*//; $Path = $module; say "Loading $module..."; verbose "Loading $module..."; # mklibdir; # my $srcdir = mksrcdir $module; mkdir($Sourcedir, 0755) unless -d $Sourcedir; # next MODULE unless $srcdir; # $Sourcedir = "src"; # next MODULE unless startmanifest; # next MODULE unless startmakefile; next MODULE unless startmodulecode; startiheader; my(@classes) = list $Path; verbose "\n"; for my $class (@classes) { $Class = $class; readmodule $class; } for my $class (@classes) { $Class = $class; writemodule $class; } say "\n"; for my $class (keys %LinkList) { $Class = $class; if(info->virtual) { my $info = $Info{$class}; my $ifndef; open VHEADER, ">$Sourcedir/pig_${class}_v.h"; $ifndef = uc("pig_${class}_v_h"); vheader "#ifndef $ifndef\n"; vheader "#define $ifndef\n\n"; if($info->virtual > 1) { for my $super ($info->virtual) { vheader qq'#include "pig_${super}_v.h"\n' if $super ne $class; } vheader "\n"; } else { vheader qq'#include "$VirtualHeader"\n\n' if $info->virtual == 1; } getvirtual; # writevheader; write_virtual_methods_def; write_virtual_class 1; vheader "#endif $ifndef\n"; close VHEADER; } } endiheader; endmodulecode; } if(exists $args{'Source'} && ref $args{'Source'}) { ${$args{'Source'}} = \@sourcefiles; } } 1;