From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#!/usr/bin/perl
use strict;
use Fcntl;
use Carp;
use vars qw/$OUT_EXT @IN @OUT/;
$OUT_EXT = 'pm';
require 'poxargs.pl';
my $domain = $ENV{POP_SYSTEM} or
croak "Set POP_SYSTEM.";
my $pkg = "${domain}::";
my $p = new POP::POX_parser;
for (my $i; $i < @IN; $i++) {
unless (sysopen(OUT, $OUT[$i], O_WRONLY|O_CREAT|O_TRUNC, 0660)) {
croak "Couldn't open [$OUT[$i]] for writing: $!";
}
print STDERR "Converting $IN[$i] to $OUT[$i]\n";
my $c;
eval {
$c = $p->parse($IN[$i]);
};
if ($@) {
print STDERR $@;
next;
}
&strip_space($c->{'comments'});
print OUT <<QQ_PM_QQ;
=head1 CLASS
Title: $c->{'name'}
Desc: $c->{'comments'}
XML: $c->{'version'}
=cut
package $pkg$c->{'name'};
QQ_PM_QQ
print OUT q|$VERSION = do{my(@r)=q$|.
q|Revision: 1.1.1.1 $=~/\d+/g;sprintf '%d.'.'%02d'x$#r,@r};|;
print OUT<<'Q_PM_Q';
use strict;
use vars qw/@ISA $VERSION/;
use Carp;
use POP::Persistent;
Q_PM_Q
my @isa = map { s/^(?!$pkg)/$pkg/o; $_ } split /\s*,\s*/, $c->{'isa'};
foreach (@isa) {
print OUT "use $_;\n";
}
print OUT "\n\@ISA = qw/", (join ' ', 'POP::Persistent', @isa), "/;\n\n";
print OUT "# PUBLIC METHODS\n";
print OUT<<'Q_PM_Q';
sub initialize {
my $this = shift;
Q_PM_Q
foreach my $p_name (sort keys %{$c->{'participants'}}) {
my $part = $c->{'participants'}{$p_name};
print OUT " \$this->{'$p_name'} = \\do{my \$a};\n";
}
foreach my $a_name (sort keys %{$c->{'attributes'}}) {
my $attribute = $c->{'attributes'}{$a_name};
if ($attribute->{'hash'}) {
print OUT " \$this->{'$a_name'} = \$this->".
"_POP__Persistent_hash_from_db(\n\t'$attribute->{'val_type'}',".
" '$a_name', {});\n";
} elsif ($attribute->{'list'}) {
# XXX - OK, this isn't good; we shouldn't be calling this private
# method, and it has the wrong name anyway. But it does the Right Thing.
print OUT " \$this->{'$a_name'} = \$this->".
"_POP__Persistent_list_from_db(\n\t'$attribute->{'type'}',".
" '$a_name');\n";
} else {
print OUT " \$this->{'$a_name'} = ",
$attribute->{'default'} ||
($attribute->{'type'} =~ /::/ ?
'\do{my $a}' :
"''"),
";\n";
}
}
print OUT "}\n";
foreach my $c_name (sort keys %{$c->{'constructors'}}) {
my $constructor = $c->{'constructors'}{$c_name};
&strip_space($constructor->{'comments'});
print OUT<<QQ_PM_QQ;
=head2 CONSTRUCTOR
Title: $pkg$c->{'name'}::$c_name
Desc: Constructor - $constructor->{'comments'}
=cut
sub $c_name {
my \$type = shift;
\$type = ref(\$type) || \$type;
QQ_PM_QQ
print OUT &param_list(values %{$constructor->{'params'}})."\n";
print OUT "\n}\n";
}
foreach my $cm_name (sort keys %{$c->{'class-methods'}}) {
my $class_method = $c->{'class-methods'}{$cm_name};
&strip_space($class_method->{'comments'});
print OUT<<QQ_PM_QQ;
=head2 CLASS METHOD
Title: $pkg$c->{'name'}::$cm_name
Desc: $class_method->{'comments'}
=cut
sub $cm_name {
my \$type = shift;
\$type = ref(\$type) || \$type;
QQ_PM_QQ
print OUT &param_list(values %{$class_method->{'params'}})."\n";
print OUT "\n}\n";
}
foreach my $p_name (sort keys %{$c->{'participants'}}) {
my $part = $c->{'participants'}{$p_name};
&strip_space($part->{'comments'});
print OUT <<QQ_PM_QQ;
=head2 CLASS METHOD
Title: $pkg$c->{'name'}::all_with_$p_name
Desc: Returns list of $c->{'name'} objects that have the given
$part->{'type'} as a $p_name
=cut
sub all_with_$p_name {
my(\$type, \$obj) = \@_;
return map {\$type->new(\$_)}
\$type->all({'where' => [['$p_name', '=', \$obj]]});
}
=head2 PARTICIPANT
Title: $pkg$c->{'name'}::$p_name
Desc: $part->{'comments'}
=cut
sub $p_name {
my \$this = shift;
if (\@_) {
my \$obj = shift;
unless (ref(\$obj) && \$obj->isa('$part->{'type'}')) {
croak "[\$obj] is not a $part->{'type'}";
}
\$this->{'$p_name'} = \\\$obj;
}
\${\$this->{'$p_name'}};
}
QQ_PM_QQ
}
foreach my $a_name (sort keys %{$c->{'attributes'}}) {
my $attr = $c->{'attributes'}{$a_name};
&strip_space($attr->{'comments'});
print OUT<<QQ_PM_QQ;
=head2 ACCESSOR
Title: $pkg$c->{'name'}::$a_name
Desc: $attr->{'comments'}
=cut
sub $a_name {
my \$this = shift;
if (\@_) {
QQ_PM_QQ
if ($attr->{'hash'}) {
print OUT ' my %hash = @_;',"\n";
if ($attr->{'val_type'} =~ /::/) { # Holds objects
print OUT ' while (my($k,$v) = %hash) {',"\n",
' unless (ref($v) && $v->isa(\'',$attr->{'val_type'},
'\')) {',"\n",
' croak "[$v] is not a ',$attr->{'val_type'},'";',"\n",
' }',"\n",
' }',"\n";
}
print OUT " \$this->{'$a_name'} = \\\%hash;\n";
print OUT<<QQ_PM_QQ;
}
wantarray ? \%{\$this->{'$a_name'}} : \$this->{'$a_name'};
}
QQ_PM_QQ
} elsif ($attr->{'list'}) {
if ($attr->{'type'} =~ /::/) { # Holds objects
print OUT ' foreach (@_) {',"\n",
' unless (ref($_) && $_->isa(\'',$attr->{'type'},
'\')) {',"\n",
' croak "[$_] is not a ',$attr->{'type'},'";',"\n",
' }',"\n",
' }',"\n";
}
print OUT " \$this->{'$a_name'} = [\@_];\n";
print OUT<<QQ_PM_QQ;
}
wantarray ? \@{\$this->{'$a_name'}} : \$this->{'$a_name'};
}
QQ_PM_QQ
} else {
print OUT ' my $obj = shift;',"\n";
if ($attr->{'type'} =~ /::/) { # Holds an object
print OUT ' unless (ref($obj) && $obj->isa(\'',$attr->{'type'},
'\')) {',"\n",
' croak "[$obj] is not a ',$attr->{'type'},'";',"\n",
' }',"\n";
print OUT " \$this->{'$a_name'} = \\\$obj\n";
print OUT<<QQ_PM_QQ;
}
\${\$this->{'$a_name'}};
}
QQ_PM_QQ
} else {
print OUT<<QQ_PM_QQ;
\$this->{'$a_name'} = \$obj;
}
\$this->{'$a_name'};
}
QQ_PM_QQ
}
}
}
foreach my $m_name (sort keys %{$c->{'methods'}}) {
my $method = $c->{'methods'}{$m_name};
&strip_space($method->{'comments'});
print OUT<<QQ_PM_QQ;
=head2 METHOD
Title: $pkg$c->{'name'}::$m_name
Desc: $method->{'comments'}
=cut
sub $m_name {
my \$this = shift;
QQ_PM_QQ
print OUT &param_list(values %{$method->{'params'}});
print OUT "\n}\n";
}
print OUT "\n\$VERSION = \$VERSION;\n";
}
sub param_list {
my @params = sort {$a->{'pos'} <=> $b->{'pos'}} @_;
my $param = ' my(';
my $scalar_param_sel;
my $scalar_param_cnt;
my $scalar_param_idx;
my $all_params_are_scalars = 1;
for (my $i=0; $i < @params; $i++) {
if ($params[$i]->{'type'} ne 'array') {
$param .= "\$$params[$i]->{'name'}, ";
vec($scalar_param_sel, $i, 1) = 1;
$scalar_param_cnt++;
$scalar_param_idx = $i; # Save the last one
} else {
$all_params_are_scalars = 0;
}
}
if ($scalar_param_cnt && $all_params_are_scalars) {
substr($param, -2) = ") = \@_;\n";
return $param;
}
if ($scalar_param_cnt == 1) {
substr($param, -2) = ') = $_['.$scalar_param_idx."];\n";
} elsif ($scalar_param_cnt) {
# Eeek. We have to convert a bit vector into a list-slice-selector
# (E.g., "01001100" --> "1,4-5")
# Just for ease of maintenance, this should perhaps be replaced with
# calls to Set::IntSpan.
my($i, $j) = (0, 0);
substr($param, -2) = ') = @_['.
join(',', grep {$_}
map {$i=$j;$j+=length();
if ($_+0) {
"$i".($j>$i+1 ? "-".($j-1) : "")
}}
split /(0+)/,
unpack("b*", $scalar_param_sel))."];\n";
} else { # no params
$param = '';
}
# Now the non-scalar params
for (my $i=0; $i < @params; $i++) {
if ($params[$i]->{'list'}) {
$param .= " my \$$params[$i]->{'name'}__ref = \$_[$i]\n".
" my \@$params[$i]->{'name'} = \@\$$params[$i]->{'name'}__ref;\n";
# my @foo = @$foo__ref;
}
}
return $param;
}
sub strip_space {
$_[0] =~ s/^\s+//;
$_[0] =~ s/\s+$//;
$_[0] =~ s/\s{4,}/\n\t/g;
}