#! perl
#
# This file is part of Deliantra, the Roguelike Realtime MMORPG.
#
# Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
#
# Deliantra is free software: you can redistribute it and/or modify it under
# the terms of the Affero GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the Affero GNU General Public License
# and the GNU General Public License along with this program. If not, see
# .
#
# The authors can be reached via e-mail to
#
# used to automatically create accessors/mutators and method interfaces
# to C++ classes for Perl.
# also write data structures as JSON object, for reflection
use JSON::XS;
my $class = shift;
my $curclass = "";
my (@scalar_member, @array_member);
# convert c member name to perl
sub c2perl($) {
local $_ = shift;
s/^([^.]+)\.\1_/$1\_/g; # tcpi.tcpi_xxx => tcpi_xxx
$_
}
for my $file (@ARGV) {
open my $fh, "<:utf8", $file
or die "$file: $!";
while (<$fh>) {
next if /^\s*\//; # skip lines starting with /
if ($curclass eq $class) {
while (/ACC\s*\(R([WO])\s*,\s*([^)\]]+)(?:\[(\S+)\])?\)/g) {
my ($access, $name, $count) = ($1, $2, $3);
my $static = /\bstatic\b/;
if ($count) {
die "static array members not yet supported by genacc" if $static;
push @array_member, [$access, $name, $static, $count];
} else {
push @scalar_member, [$access, $name, $static];
}
}
if (/MTH \s+ ([^(]*?) \s* ([A-Za-z_0-9]+) \s* \(([^)]*)\)/x) {
my ($retval, $name, $args) = ($1, $2, $3);
push @method_member, [$retval, $name, $args];
}
}
while (/INTERFACE_CLASS\s*\((\S+)\)/g) {
$curclass = $1;
}
}
}
for (@method_member) {
my ($rettype, $name, $params) = @$_;
if ($rettype =~ s/static\s+//) {
my $args = join ", ", $params =~ m/.*?([a-zA-Z_0-9]+)(?:,\s*|$)/g;
if ($rettype ne "void") {
print "$rettype\n$name ($params)\n",
"\tCODE:\n",
"\tRETVAL = $class\::$name ($args);\n",
"\tOUTPUT:\n",
"\tRETVAL\n";
} else {
print "$rettype\n$name ($params)\n",
"\tCODE:\n",
"\t$class\::$name ($args);\n",
}
} else {
print "$rettype\n$class\::$name ($params)\n";
}
print "\n";
}
for my $static (0, 1) {
my @member = grep $static == $_->[2], @scalar_member;
if (@member) {
my $self;
if ($static) {
$self = "$class\::";
print "SV *$member[0][1] (SV *newval = 0)\n",
"\tPROTOTYPE: ;\$\n";
} else {
$self = "self->";
print "SV *$member[0][1] ($class *self, SV *newval = 0)\n",
"\tPROTOTYPE: \$;\$\n";
}
if (@member > 1) {
print "\tALIAS:\n";
for (1 .. $#member) {
print "\t\t" . (c2perl $member[$_][1]) . "\t= $_\n";
}
}
print "\tCODE:\n";
my $ix = @member == 1 ? 0 : "ix";
# read
print "\tif (GIMME_V == G_VOID)\n",
"\t RETVAL = &PL_sv_undef;\n",
"\telse\n",
"\t switch ($ix)\n",
"\t {\n",
(map "\t case $_: RETVAL = to_sv ($self$member[$_][1]); break;\n",
0 .. $#member),
"\t default: croak (\"scalar_member is write-only\");\n",
"\t };\n";
# write
print "\tif (newval)\n",
"\t switch ($ix)\n",
"\t {\n",
(map "\t case $_: sv_to (newval, $self$member[$_][1]); break;\n",
grep $member[$_][0] eq "W",
0 .. $#member),
"\t default: croak (\"scalar_member is read-only\");\n",
"\t };\n";
print "\tOUTPUT: RETVAL\n\n";
}
}
if (@array_member) {
print "SV *$array_member[0][1] ($class *self, int idx, SV *newval = 0)\n";
print "\tPROTOTYPE: \$;\$\n";
if (@array_member > 1) {
print "\tALIAS:\n";
for (1 .. $#array_member) {
print "\t\t" . (c2perl $array_member[$_][1]) . "\t= $_\n";
}
}
print "\tCODE:\n";
print "\tif (idx < 0) croak (\"negative array index\");\n";
my $ix = @array_member == 1 ? 0 : "ix";
# range
print "\t switch ($ix)\n",
"\t {\n",
(map "\t case $_: if (idx >= $array_member[$_][3]) croak (\"array index out of bounds\"); break;\n",
0 .. $#array_member),
"\t };\n";
# read
print "\tif (GIMME_V == G_VOID)\n",
"\t RETVAL = &PL_sv_undef;\n",
"\telse\n",
"\t switch ($ix)\n",
"\t {\n",
(map "\t case $_: RETVAL = to_sv (self->$array_member[$_][1] [idx]); break;\n",
0 .. $#array_member),
"\t default: croak (\"array_member is write-only\");\n",
"\t };\n";
# write
print "\tif (newval)\n",
"\t switch ($ix)\n",
"\t {\n",
(map "\t case $_: sv_to (newval, self->$array_member[$_][1] [idx]); break;\n",
grep $array_member[$_][0] eq "W",
0 .. $#array_member),
"\t default: croak (\"array_member is read-only\");\n",
"\t };\n";
print "\tOUTPUT: RETVAL\n\n";
}
my $json = JSON::XS->new->utf8->encode ({
class => $class,
methods => { map +($_->[1], [$_->[0], $_->[2]]), @method_member },
scalars => { map +($_->[1], [$_->[0] ]), @scalar_member },
arrays => { map +($_->[1], [$_->[0], $_->[2]]), @array_member },
});
$json = join "\n", map {
my $part = $_;
$part =~ s/(["\\])/\\$1/g;
"\"$part\""
} unpack "(a80)*", $json;
print "BOOT:\n",
"\tav_push (av_reflect, newSVpv ($json, 0));\n\n";